From 5edc36c17be3c5040c7eb70ffe2787fccbe46388 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 11 Jun 2024 16:19:04 +0400 Subject: [PATCH] feat: support volume configuration, provisioning, etc This is early WIP. See #8367 Signed-off-by: Andrey Smirnov --- api/prototool.yaml | 2 +- api/resource/definitions/block/block.proto | 105 + api/resource/definitions/enums/enums.proto | 38 + .../google/api/expr/v1alpha1/checked.proto | 295 ++ .../google/api/expr/v1alpha1/eval.proto | 113 + .../google/api/expr/v1alpha1/explain.proto | 49 + .../google/api/expr/v1alpha1/syntax.proto | 344 ++ .../google/api/expr/v1alpha1/value.proto | 101 + cmd/installer/pkg/install/errata.go | 34 +- cmd/installer/pkg/install/install.go | 404 +- cmd/installer/pkg/install/manifest.go | 545 --- cmd/installer/pkg/install/manifest_test.go | 328 -- cmd/installer/pkg/install/target.go | 371 -- cmd/installer/pkg/install/target_test.go | 54 - cmd/installer/pkg/install/verify.go | 65 - cmd/talosctl/cmd/mgmt/cluster/create.go | 2 +- cmd/talosctl/cmd/talos/upgrade.go | 5 + go.mod | 8 +- go.sum | 10 +- hack/structprotogen/proto/proto.go | 2 + .../server/v1alpha1/v1alpha1_server.go | 70 +- internal/app/machined/main.go | 8 + .../pkg/controllers/block/discovery.go | 52 +- .../machined/pkg/controllers/block/disks.go | 1 + .../block/internal/volumes/disk.go | 100 + .../block/internal/volumes/encrypt.go | 109 + .../block/internal/volumes/format.go | 111 + .../block/internal/volumes/grow.go | 88 + .../block/internal/volumes/locate.go | 159 + .../block/internal/volumes/partition.go | 117 + .../block/internal/volumes/proto.go | 22 + .../block/internal/volumes/volumes.go | 56 + .../pkg/controllers/block/system_disk.go | 7 +- .../pkg/controllers/block/system_disk_test.go | 54 + .../pkg/controllers/block/user_disk_config.go | 156 + .../block/user_disk_config_test.go | 141 + .../pkg/controllers/block/volume_config.go | 287 ++ .../controllers/block/volume_config_test.go | 184 + .../pkg/controllers/block/volume_manager.go | 390 ++ .../pkg/controllers/cri/runc_memfd_bind.go | 4 +- .../app/machined/pkg/runtime/sequencer.go | 4 +- internal/app/machined/pkg/runtime/state.go | 4 - .../runtime/v1alpha1/bootloader/bootloader.go | 25 +- .../v1alpha1/bootloader/grub/boot_label.go | 5 - .../runtime/v1alpha1/bootloader/grub/grub.go | 12 +- .../v1alpha1/bootloader/grub/install.go | 112 +- .../runtime/v1alpha1/bootloader/grub/probe.go | 57 +- .../v1alpha1/bootloader/grub/revert.go | 68 +- .../v1alpha1/bootloader/mount/mount.go | 128 +- .../v1alpha1/bootloader/options/options.go | 19 + .../v1alpha1/bootloader/sdboot/sdboot.go | 233 +- .../v1alpha1/v1alpha1_controller_test.go | 24 +- .../runtime/v1alpha1/v1alpha1_sequencer.go | 27 +- .../v1alpha1/v1alpha1_sequencer_tasks.go | 637 ++- .../pkg/runtime/v1alpha1/v1alpha1_state.go | 113 +- .../runtime/v1alpha2/v1alpha2_controller.go | 3 + .../pkg/runtime/v1alpha2/v1alpha2_state.go | 5 + internal/app/machined/revert.go | 3 +- internal/app/maintenance/server.go | 34 +- internal/integration/provision/provision.go | 2 +- internal/pkg/encryption/encryption.go | 209 +- internal/pkg/encryption/keys/keys.go | 36 +- internal/pkg/encryption/keys/kms.go | 6 +- internal/pkg/encryption/keys/nodeid.go | 4 +- internal/pkg/encryption/keys/options.go | 8 +- internal/pkg/encryption/keys/static.go | 4 +- internal/pkg/encryption/keys/tpm2.go | 6 +- internal/pkg/etcd/etcd.go | 8 +- internal/pkg/meta/meta.go | 80 +- internal/pkg/mount/system.go | 244 +- internal/pkg/mount/v2/mount.go | 268 ++ internal/pkg/mount/v2/points.go | 57 + internal/pkg/mount/v2/repair.go | 24 + internal/pkg/mount/v2/unmount.go | 76 + internal/pkg/partition/constants.go | 15 +- internal/pkg/partition/format.go | 29 +- internal/pkg/partition/format_test.go | 157 - internal/pkg/partition/partition.go | 119 +- internal/pkg/partition/wipe.go | 58 + pkg/imager/out.go | 33 +- pkg/imager/utils/loopback.go | 30 - .../resource/definitions/block/block.pb.go | 1580 ++++++- .../definitions/block/block_vtproto.pb.go | 4094 +++++++++++++++-- .../resource/definitions/enums/enums.pb.go | 429 +- pkg/machinery/cel/cel.go | 159 + pkg/machinery/cel/cel_test.go | 66 + pkg/machinery/cel/celenv/celenv.go | 46 + pkg/machinery/cel/celenv/celenv_test.go | 64 + pkg/machinery/compatibility/talos_version.go | 11 + .../types/v1alpha1/v1alpha1_provider.go | 2 +- pkg/machinery/go.mod | 6 +- pkg/machinery/go.sum | 10 + pkg/machinery/imager/quirks/quirks.go | 12 + pkg/machinery/resources/block/block.go | 49 +- pkg/machinery/resources/block/block_test.go | 5 + .../resources/block/deep_copy.generated.go | 46 +- .../resources/block/discovered_volume.go | 30 +- .../block/discovery_refresh_request.go | 65 + .../block/discovery_refresh_status.go | 62 + pkg/machinery/resources/block/disk.go | 10 +- .../resources/block/encryptionkeytype.go | 18 + .../resources/block/encryptionprovidertype.go | 16 + .../resources/block/filesystemtype.go | 16 + pkg/machinery/resources/block/system_disk.go | 3 +- .../block/user_disk_config_status.go | 65 + .../resources/block/volume_config.go | 181 + .../resources/block/volume_status.go | 88 + pkg/machinery/resources/block/volumephase.go | 21 + pkg/machinery/resources/block/volumetype.go | 17 + .../resources/block/volumetype_enumer.go | 450 ++ pkg/provision/providers/qemu/launch.go | 23 +- website/content/v1.8/reference/api.md | 318 ++ website/content/v1.8/reference/cli.md | 1 - 113 files changed, 12195 insertions(+), 3745 deletions(-) create mode 100644 api/vendor/google/api/expr/v1alpha1/checked.proto create mode 100644 api/vendor/google/api/expr/v1alpha1/eval.proto create mode 100644 api/vendor/google/api/expr/v1alpha1/explain.proto create mode 100644 api/vendor/google/api/expr/v1alpha1/syntax.proto create mode 100644 api/vendor/google/api/expr/v1alpha1/value.proto delete mode 100644 cmd/installer/pkg/install/manifest.go delete mode 100644 cmd/installer/pkg/install/manifest_test.go delete mode 100644 cmd/installer/pkg/install/target.go delete mode 100644 cmd/installer/pkg/install/target_test.go delete mode 100644 cmd/installer/pkg/install/verify.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/disk.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/encrypt.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/format.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/grow.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/locate.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/partition.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/proto.go create mode 100644 internal/app/machined/pkg/controllers/block/internal/volumes/volumes.go create mode 100644 internal/app/machined/pkg/controllers/block/system_disk_test.go create mode 100644 internal/app/machined/pkg/controllers/block/user_disk_config.go create mode 100644 internal/app/machined/pkg/controllers/block/user_disk_config_test.go create mode 100644 internal/app/machined/pkg/controllers/block/volume_config.go create mode 100644 internal/app/machined/pkg/controllers/block/volume_config_test.go create mode 100644 internal/app/machined/pkg/controllers/block/volume_manager.go create mode 100644 internal/pkg/mount/v2/mount.go create mode 100644 internal/pkg/mount/v2/points.go create mode 100644 internal/pkg/mount/v2/repair.go create mode 100644 internal/pkg/mount/v2/unmount.go delete mode 100644 internal/pkg/partition/format_test.go create mode 100644 internal/pkg/partition/wipe.go delete mode 100644 pkg/imager/utils/loopback.go create mode 100644 pkg/machinery/cel/cel.go create mode 100644 pkg/machinery/cel/cel_test.go create mode 100644 pkg/machinery/cel/celenv/celenv.go create mode 100644 pkg/machinery/cel/celenv/celenv_test.go create mode 100644 pkg/machinery/resources/block/discovery_refresh_request.go create mode 100644 pkg/machinery/resources/block/discovery_refresh_status.go create mode 100644 pkg/machinery/resources/block/encryptionkeytype.go create mode 100644 pkg/machinery/resources/block/encryptionprovidertype.go create mode 100644 pkg/machinery/resources/block/filesystemtype.go create mode 100644 pkg/machinery/resources/block/user_disk_config_status.go create mode 100644 pkg/machinery/resources/block/volume_config.go create mode 100644 pkg/machinery/resources/block/volume_status.go create mode 100644 pkg/machinery/resources/block/volumephase.go create mode 100644 pkg/machinery/resources/block/volumetype.go create mode 100644 pkg/machinery/resources/block/volumetype_enumer.go diff --git a/api/prototool.yaml b/api/prototool.yaml index cff0c9a0f9e..609c328ce09 100644 --- a/api/prototool.yaml +++ b/api/prototool.yaml @@ -8,7 +8,7 @@ lint: ignores: - id: FILE_OPTIONS_GO_PACKAGE_NOT_LONG_FORM files: - - vendor/google/rpc/status.proto + - vendor/google/ rules: # The specific linters to add. diff --git a/api/resource/definitions/block/block.proto b/api/resource/definitions/block/block.proto index f941c71265c..80bff129a99 100755 --- a/api/resource/definitions/block/block.proto +++ b/api/resource/definitions/block/block.proto @@ -5,6 +5,9 @@ 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; @@ -35,6 +38,23 @@ message DiscoveredVolumeSpec { string type = 14; string device_path = 15; string parent = 16; + string dev_path = 17; + string parent_dev_path = 18; +} + +// 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; } // DiskSpec is the spec for Disks status. @@ -52,10 +72,95 @@ message DiskSpec { string transport = 11; bool rotational = 12; bool cdrom = 13; + string dev_path = 14; +} + +// 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; +} + +// 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; +} + +// MountSpec is the spec for volume mount. +message MountSpec { + string target_path = 1; +} + +// 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; +} + +// ProvisioningSpec is the spec for volume provisioning. +message ProvisioningSpec { + DiskSelector disk_selector = 1; + PartitionSpec partition_spec = 2; + int64 wave = 3; + FilesystemSpec filesystem_spec = 4; } // SystemDiskSpec is the spec for SystemDisks status. message SystemDiskSpec { string disk_id = 1; + string dev_path = 2; +} + +// UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus status. +message UserDiskConfigStatusSpec { + bool ready = 1; +} + +// 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; +} + +// 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; } diff --git a/api/resource/definitions/enums/enums.proto b/api/resource/definitions/enums/enums.proto index 22da936de51..27a9e25dd28 100755 --- a/api/resource/definitions/enums/enums.proto +++ b/api/resource/definitions/enums/enums.proto @@ -354,6 +354,44 @@ enum NethelpersVLANProtocol { VLAN_PROTOCOL8021_AD = 34984; } +// 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; +} + +// 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; +} + +// BlockVolumeType describes volume type. +enum BlockVolumeType { + VOLUME_TYPE_PARTITION = 0; + VOLUME_TYPE_DISK = 1; + VOLUME_TYPE_TMPFS = 2; +} + // KubespanPeerState is KubeSpan peer current state. enum KubespanPeerState { PEER_STATE_UNKNOWN = 0; diff --git a/api/vendor/google/api/expr/v1alpha1/checked.proto b/api/vendor/google/api/expr/v1alpha1/checked.proto new file mode 100644 index 00000000000..b2ba161782b --- /dev/null +++ b/api/vendor/google/api/expr/v1alpha1/checked.proto @@ -0,0 +1,295 @@ +// 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; +} diff --git a/api/vendor/google/api/expr/v1alpha1/eval.proto b/api/vendor/google/api/expr/v1alpha1/eval.proto new file mode 100644 index 00000000000..6ad7a3da5a3 --- /dev/null +++ b/api/vendor/google/api/expr/v1alpha1/eval.proto @@ -0,0 +1,113 @@ +// 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 inital, 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; +} diff --git a/api/vendor/google/api/expr/v1alpha1/explain.proto b/api/vendor/google/api/expr/v1alpha1/explain.proto new file mode 100644 index 00000000000..e533f8be820 --- /dev/null +++ b/api/vendor/google/api/expr/v1alpha1/explain.proto @@ -0,0 +1,49 @@ +// 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; +} diff --git a/api/vendor/google/api/expr/v1alpha1/syntax.proto b/api/vendor/google/api/expr/v1alpha1/syntax.proto new file mode 100644 index 00000000000..7cdc188f465 --- /dev/null +++ b/api/vendor/google/api/expr/v1alpha1/syntax.proto @@ -0,0 +1,344 @@ +// 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; +} diff --git a/api/vendor/google/api/expr/v1alpha1/value.proto b/api/vendor/google/api/expr/v1alpha1/value.proto new file mode 100644 index 00000000000..a34d6819d3e --- /dev/null +++ b/api/vendor/google/api/expr/v1alpha1/value.proto @@ -0,0 +1,101 @@ +// 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 represenation + // cannot be used. + repeated Entry entries = 1; +} diff --git a/cmd/installer/pkg/install/errata.go b/cmd/installer/pkg/install/errata.go index ab0da0c43ef..efe7c5e3b64 100644 --- a/cmd/installer/pkg/install/errata.go +++ b/cmd/installer/pkg/install/errata.go @@ -73,33 +73,28 @@ func errataArm64ZBoot() { // 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() error { +func (i *Installer) errataNetIfnames(talosVersion *compatibility.TalosVersion) { if i.cmdline.Get(constants.KernelParamNetIfnames).First() != nil { // net.ifnames is already set, nothing to do - return nil + return } - oldTalos, err := upgradeFromPreIfnamesTalos() - if err != nil { - return err - } + oldTalos := upgradeFromPreIfnamesTalos(talosVersion) if oldTalos { log.Printf("appending net.ifnames=0 to the kernel command line") i.cmdline.Append(constants.KernelParamNetIfnames, "0") } - - return nil } -func upgradeFromPreIfnamesTalos() (bool, error) { +func readHostTalosVersion() (*compatibility.TalosVersion, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() if _, err := os.Stat(constants.MachineSocketPath); err != nil { - // old Talos version, include fallback - return true, nil + // can't read Talos version + return nil, nil } c, err := client.New(ctx, @@ -107,7 +102,7 @@ func upgradeFromPreIfnamesTalos() (bool, error) { client.WithGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())), ) if err != nil { - return false, fmt.Errorf("error connecting to the machine service: %w", err) + return nil, fmt.Errorf("error connecting to the machine service: %w", err) } defer c.Close() //nolint:errcheck @@ -117,15 +112,24 @@ func upgradeFromPreIfnamesTalos() (bool, error) { resp, err := c.Version(ctx) if err != nil { - return false, fmt.Errorf("error getting Talos version: %w", err) + return nil, fmt.Errorf("error getting Talos version: %w", err) } hostVersion := unpack(resp.Messages) talosVersion, err := compatibility.ParseTalosVersion(hostVersion.Version) if err != nil { - return false, fmt.Errorf("error parsing Talos version: %w", err) + 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(), nil + return talosVersion.DisablePredictableNetworkInterfaces() } diff --git a/cmd/installer/pkg/install/install.go b/cmd/installer/pkg/install/install.go index 7e2a4442fa6..449470e8b2a 100644 --- a/cmd/installer/pkg/install/install.go +++ b/cmd/installer/pkg/install/install.go @@ -12,12 +12,14 @@ import ( "log" "os" "path/filepath" - "syscall" - "time" - "github.com/siderolabs/go-blockdevice/blockdevice" + "github.com/google/uuid" + "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" - "github.com/siderolabs/go-retry/retry" "gopkg.in/yaml.v3" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" @@ -25,9 +27,11 @@ import ( "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" 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/mount" + "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/imager/overlay/executor" + "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" "github.com/siderolabs/talos/pkg/machinery/overlay" "github.com/siderolabs/talos/pkg/machinery/version" @@ -76,6 +80,8 @@ func (m Mode) IsImage() bool { return m == ModeImage } +const typeGPT = "gpt" + // Install installs Talos. // //nolint:gocyclo @@ -174,10 +180,8 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options) // Installer represents the installer logic. It serves as the entrypoint to all // installation methods. type Installer struct { - cmdline *procfs.Cmdline - options *Options - manifest *Manifest - bootloader bootloader.Bootloader + cmdline *procfs.Cmdline + options *Options } // NewInstaller initializes and returns an Installer. @@ -195,30 +199,15 @@ func NewInstaller(ctx context.Context, cmdline *procfs.Cmdline, mode Mode, opts i.options.Printf = log.Printf } - if !i.options.Zero { - i.bootloader, err = bootloader.Probe(ctx, i.options.Disk) - if err != nil && !os.IsNotExist(err) { - return nil, fmt.Errorf("failed to probe bootloader: %w", err) - } + if mode == ModeUpgrade && i.options.Force { + i.options.Printf("system disk wipe on upgrade is not supported anymore, option ignored") } - i.options.BootAssets.FillDefaults(opts.Arch) - - bootLoaderPresent := i.bootloader != nil - if !bootLoaderPresent { - if mode.IsImage() { - // on image creation, use the bootloader based on options - i.bootloader = bootloader.New(opts.ImageSecureboot, opts.Version) - } else { - // on install/upgrade perform automatic detection - i.bootloader = bootloader.NewAuto() - } + if i.options.Zero && mode != ModeInstall { + i.options.Printf("zeroing of the disk is only supported for the initial installation, option ignored") } - i.manifest, err = NewManifest(mode, i.bootloader.UEFIBoot(), bootLoaderPresent, i.options) - if err != nil { - return nil, fmt.Errorf("failed to create installation manifest: %w", err) - } + i.options.BootAssets.FillDefaults(opts.Arch) return i, nil } @@ -228,93 +217,111 @@ func NewInstaller(ctx context.Context, cmdline *procfs.Cmdline, mode Mode, opts // //nolint:gocyclo,cyclop func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { - errataArm64ZBoot() + // pre-flight checks, erratas + hostTalosVersion, err := readHostTalosVersion() + if err != nil { + return err + } if mode == ModeUpgrade { - if err = i.errataNetIfnames(); err != nil { - return err - } + errataArm64ZBoot() + + i.errataNetIfnames(hostTalosVersion) } if err = i.runPreflightChecks(mode); err != nil { return err } + // prepare extensions if legacy machine.install.extensions is present if err = i.installExtensions(); err != nil { return err } - if err = i.manifest.Execute(); err != nil { - return err + // open and lock the blockdevice for the installation disk for the whole duration of the installer + bd, err := block.NewFromPath(i.options.Disk, block.OpenForWrite()) + if err != nil { + return fmt.Errorf("failed to open blockdevice %s: %w", i.options.Disk, err) } - // Mount the partitions. - mountpoints := mount.NewMountPoints() + defer bd.Close() //nolint:errcheck - var bootLabels []string - - if i.bootloader.UEFIBoot() { - bootLabels = []string{constants.EFIPartitionLabel} - } else { - bootLabels = []string{constants.BootPartitionLabel, constants.EFIPartitionLabel} + if err = bd.Lock(true); err != nil { + return fmt.Errorf("failed to lock blockdevice %s: %w", i.options.Disk, err) } - for _, label := range bootLabels { - if err = func() error { - var device string - // searching targets for the device to be used - OuterLoop: - for dev, targets := range i.manifest.Targets { - for _, target := range targets { - if target.Label == label { - device = dev + defer bd.Unlock() //nolint:errcheck - break OuterLoop - } - } - } - - if device == "" { - return fmt.Errorf("failed to detect %s target device", label) - } + var bootlder bootloader.Bootloader - var bd *blockdevice.BlockDevice + // if running in install/image mode, just create new GPT partition table + // if running in upgrade mode, verify that install disk has correct GPT partition table and expected partitions - bd, err = retryBlockdeviceOpen(device) - if err != nil { - return fmt.Errorf("error opening blockdevice %s: %w", device, err) - } - - defer bd.Close() //nolint:errcheck + // probe the disk anyways + info, err := blkid.Probe(bd.File(), blkid.WithSkipLocking(true)) + if err != nil { + return fmt.Errorf("failed to probe blockdevice %s: %w", i.options.Disk, err) + } - var mountpoint *mount.Point + gptdev, err := gpt.DeviceFromBlockDevice(bd) + if err != nil { + return fmt.Errorf("error getting GPT device: %w", err) + } - mountpoint, err = mount.SystemMountPointForLabel(ctx, bd, label, mount.WithPrefix(i.options.MountPrefix)) - if err != nil { - return fmt.Errorf("error finding system mount points for %s: %w", device, err) + switch mode { + case ModeImage: + // on image creation, we don't care about disk contents + bootlder = bootloader.New(i.options.ImageSecureboot, i.options.Version) + 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.Disk, info.Name) } + } else { + // zero the disk + if err = bd.FastWipe(); err != nil { + return fmt.Errorf("failed to zero blockdevice %s: %w", i.options.Disk, err) + } + } - mountpoints.Set(label, mountpoint) - - return nil - }(); err != nil { - return fmt.Errorf("error mounting for label %q: %w", label, err) + // on install, automatically detect the bootloader + bootlder = bootloader.NewAuto() + 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.Disk, info.Name) } } - if err = mount.Mount(mountpoints); err != nil { - return fmt.Errorf("failed to mount standard mountpoints: %w", err) + if mode == ModeImage || mode == ModeInstall { + // create partitions and re-probe the device + info, err = i.createPartitions(gptdev, mode, hostTalosVersion, bootlder) + if err != nil { + return fmt.Errorf("failed to create partitions: %w", err) + } } - defer func() { - e := mount.Unmount(mountpoints) - if e != nil { - log.Printf("failed to unmount: %v", e) + if mode == ModeUpgrade { + // on upgrade, probe the bootloader + bootlder, err = bootloader.Probe(i.options.Disk, bootloaderoptions.ProbeOptions{ + // the disk is already locked + BlockProbeOptions: []blkid.ProbeOption{ + blkid.WithSkipLocking(true), + }, + }) + if err != nil { + return fmt.Errorf("failed to probe bootloader on upgrade: %w", err) } - }() + } // Install the bootloader. - if err = i.bootloader.Install(bootloaderoptions.InstallOptions{ + bootInstallResult, err := bootlder.Install(bootloaderoptions.InstallOptions{ BootDisk: i.options.Disk, Arch: i.options.Arch, Cmdline: i.cmdline.String(), @@ -323,43 +330,49 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { MountPrefix: i.options.MountPrefix, BootAssets: i.options.BootAssets, Printf: i.options.Printf, - }); err != nil { - return fmt.Errorf("failed to install bootloader: %w", err) - } + BlkidInfo: info, - if i.options.Board != constants.BoardNone { - var b runtime.Board + ExtraInstallStep: func() error { + if i.options.Board != constants.BoardNone { + var b runtime.Board - b, err = board.NewBoard(i.options.Board) - if err != nil { - return err - } + b, err = board.NewBoard(i.options.Board) + if err != nil { + return err + } - i.options.Printf("installing U-Boot for %q", b.Name()) - - if err = b.Install(runtime.BoardInstallOptions{ - InstallDisk: i.options.Disk, - MountPrefix: i.options.MountPrefix, - UBootPath: i.options.BootAssets.UBootPath, - DTBPath: i.options.BootAssets.DTBPath, - RPiFirmwarePath: i.options.BootAssets.RPiFirmwarePath, - Printf: i.options.Printf, - }); err != nil { - return fmt.Errorf("failed to install for board %s: %w", b.Name(), err) - } - } + i.options.Printf("installing U-Boot for %q", b.Name()) + + if err = b.Install(runtime.BoardInstallOptions{ + InstallDisk: i.options.Disk, + MountPrefix: i.options.MountPrefix, + UBootPath: i.options.BootAssets.UBootPath, + DTBPath: i.options.BootAssets.DTBPath, + RPiFirmwarePath: i.options.BootAssets.RPiFirmwarePath, + Printf: i.options.Printf, + }); err != nil { + return fmt.Errorf("failed to install for board %s: %w", b.Name(), err) + } + } - if i.options.OverlayInstaller != nil { - i.options.Printf("running overlay installer %q", i.options.OverlayName) - - if err = i.options.OverlayInstaller.Install(overlay.InstallOptions[overlay.ExtraOptions]{ - InstallDisk: i.options.Disk, - 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) - } + if i.options.OverlayInstaller != nil { + i.options.Printf("running overlay installer %q", i.options.OverlayName) + + if err = i.options.OverlayInstaller.Install(overlay.InstallOptions[overlay.ExtraOptions]{ + InstallDisk: i.options.Disk, + 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 + }, + }) + if err != nil { + return fmt.Errorf("failed to install bootloader: %w", err) } if mode == ModeUpgrade || len(i.options.MetaValues.values) > 0 { @@ -368,16 +381,10 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { metaPartitionName string ) - for _, targets := range i.manifest.Targets { - for _, target := range targets { - if target.Label == constants.MetaPartitionLabel { - metaPartitionName = target.PartitionName + for _, partition := range info.Parts { + if pointer.SafeDeref(partition.PartitionLabel) == constants.MetaPartitionLabel { + metaPartitionName = partitioning.DevName(i.options.Disk, partition.PartitionIndex) - break - } - } - - if metaPartitionName != "" { break } } @@ -393,8 +400,8 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { var ok bool if mode == ModeUpgrade { - if ok, err = metaState.SetTag(context.Background(), meta.Upgrade, i.bootloader.PreviousLabel()); !ok || err != nil { - return fmt.Errorf("failed to set upgrade tag: %q", i.bootloader.PreviousLabel()) + if ok, err = metaState.SetTag(context.Background(), meta.Upgrade, bootInstallResult.PreviousLabel); !ok || err != nil { + return fmt.Errorf("failed to set upgrade tag: %q", bootInstallResult.PreviousLabel) } } @@ -412,6 +419,123 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { return nil } +//nolint:gocyclo,cyclop +func (i *Installer) createPartitions(gptdev gpt.Device, mode Mode, hostTalosVersion *compatibility.TalosVersion, bootlder bootloader.Bootloader) (*blkid.Info, error) { + var partitionOptions *runtime.PartitionOptions + + if i.options.Board != constants.BoardNone && !quirks.New(i.options.Version).SupportsOverlay() { + var b runtime.Board + + b, err := board.NewBoard(i.options.Board) + if err != nil { + return nil, err + } + + partitionOptions = b.PartitionOptions() + } + + if i.options.OverlayInstaller != nil { + overlayOpts, getOptsErr := i.options.OverlayInstaller.GetOptions(i.options.ExtraOptions) + if getOptsErr != nil { + return nil, fmt.Errorf("failed to get overlay installer options: %w", getOptsErr) + } + + if overlayOpts.PartitionOptions.Offset != 0 { + partitionOptions = &runtime.PartitionOptions{ + PartitionsOffset: overlayOpts.PartitionOptions.Offset, + } + } + } + + var gptOptions []gpt.Option + + if partitionOptions != nil && partitionOptions.PartitionsOffset != 0 { + gptOptions = append(gptOptions, gpt.WithSkipLBAs(uint(partitionOptions.PartitionsOffset))) + } + + if i.options.LegacyBIOSSupport { + gptOptions = append(gptOptions, gpt.WithMarkPMBRBootable()) + } + + pt, err := gpt.New(gptdev, gptOptions...) + if err != nil { + return nil, fmt.Errorf("failed to initialize GPT: %w", err) + } + + // boot partitions + partitions := bootlder.RequiredPartitions() + + // META partition + partitions = append(partitions, + partition.NewPartitionOptions(constants.MetaPartitionLabel, false), + ) + + 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(constants.StatePartitionLabel, false), + ) + } + + if legacyImage { + partitions = append(partitions, + partition.NewPartitionOptions(constants.EphemeralPartitionLabel, false), + ) + } + + 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) + } + + // now format all partitions + for idx, p := range partitions { + devName := partitioning.DevName(i.options.Disk, uint(idx+1)) + + if err = partition.Format(devName, &p.FormatOptions, i.options.Printf); err != nil { + return nil, fmt.Errorf("failed to format partition %s: %w", devName, err) + } + } + + info, err := blkid.ProbePath(i.options.Disk, blkid.WithSkipLocking(true)) + if err != nil { + return nil, fmt.Errorf("failed to probe blockdevice %s: %w", i.options.Disk, err) + } + + if len(info.Parts) != len(partitions) { + return nil, fmt.Errorf("expected %d partitions, got %d", len(partitions), len(info.Parts)) + } + + // this is weird, but sometimes blkid doesn't return the filesystem type for freshly formatted partitions + for idx, p := range partitions { + if p.FormatOptions.FileSystemType == partition.FilesystemTypeNone || p.FormatOptions.FileSystemType == partition.FilesystemTypeZeroes { + continue + } + + info.Parts[idx].Name = p.FormatOptions.FileSystemType + } + + return info, nil +} + func (i *Installer) runPreflightChecks(mode Mode) error { if mode != ModeUpgrade { // pre-flight checks only apply to upgrades @@ -431,30 +555,6 @@ func (i *Installer) runPreflightChecks(mode Mode) error { return checks.Run(ctx) } -func retryBlockdeviceOpen(device string) (*blockdevice.BlockDevice, error) { - var bd *blockdevice.BlockDevice - - err := retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { - var openErr error - - bd, openErr = blockdevice.Open(device, blockdevice.WithExclusiveLock(true)) - if openErr == nil { - return nil - } - - switch { - case os.IsNotExist(openErr): - return retry.ExpectedError(openErr) - case errors.Is(openErr, syscall.ENODEV), errors.Is(openErr, syscall.ENXIO): - return retry.ExpectedError(openErr) - default: - return nil - } - }) - - return bd, err -} - func overlayPresent() bool { _, err := os.Stat(constants.ImagerOverlayInstallerDefaultPath) diff --git a/cmd/installer/pkg/install/manifest.go b/cmd/installer/pkg/install/manifest.go deleted file mode 100644 index 2cf68d38f62..00000000000 --- a/cmd/installer/pkg/install/manifest.go +++ /dev/null @@ -1,545 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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 ( - "bufio" - "context" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - "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/board" - "github.com/siderolabs/talos/internal/pkg/mount" - "github.com/siderolabs/talos/internal/pkg/partition" - "github.com/siderolabs/talos/pkg/machinery/constants" - "github.com/siderolabs/talos/pkg/machinery/imager/quirks" -) - -// Manifest represents the instructions for preparing all block devices -// for an installation. -type Manifest struct { - PartitionOptions *runtime.PartitionOptions - Devices map[string]Device - Targets map[string][]*Target - LegacyBIOSSupport bool - - Printf func(string, ...any) -} - -// Device represents device options. -type Device struct { - Device string - - ResetPartitionTable bool - Zero bool - - SkipOverlayMountsCheck bool -} - -// NewManifest initializes and returns a Manifest. -// -//nolint:gocyclo,cyclop -func NewManifest(mode Mode, uefiOnlyBoot bool, bootLoaderPresent bool, opts *Options) (manifest *Manifest, err error) { - manifest = &Manifest{ - Devices: map[string]Device{}, - Targets: map[string][]*Target{}, - LegacyBIOSSupport: opts.LegacyBIOSSupport, - - Printf: opts.Printf, - } - - if opts.Board != constants.BoardNone && !quirks.New(opts.Version).SupportsOverlay() { - if uefiOnlyBoot { - return nil, errors.New("board option can't be used with uefi-only-boot") - } - - var b runtime.Board - - b, err = board.NewBoard(opts.Board) - if err != nil { - return nil, err - } - - manifest.PartitionOptions = b.PartitionOptions() - } - - if opts.OverlayInstaller != nil { - overlayOpts, getOptsErr := opts.OverlayInstaller.GetOptions(opts.ExtraOptions) - if getOptsErr != nil { - return nil, fmt.Errorf("failed to get overlay installer options: %w", getOptsErr) - } - - if overlayOpts.PartitionOptions.Offset != 0 { - manifest.PartitionOptions = &runtime.PartitionOptions{ - PartitionsOffset: overlayOpts.PartitionOptions.Offset, - } - } - } - - // TODO: legacy, to support old Talos initramfs, assume force if boot partition not found - if !bootLoaderPresent { - opts.Force = true - } - - if !opts.Force && opts.Zero { - return nil, errors.New("zero option can't be used without force") - } - - if !opts.Force && !bootLoaderPresent { - return nil, errors.New("install with preserve is not supported if existing boot partition was not found") - } - - // Verify that the target device(s) can satisfy the requested options. - - if mode == ModeInstall { - if err = VerifyEphemeralPartition(opts); err != nil { - return nil, fmt.Errorf("failed to prepare ephemeral partition: %w", err) - } - } - - skipOverlayMountsCheck, err := shouldSkipOverlayMountsCheck(mode) - if err != nil { - return nil, err - } - - manifest.Devices[opts.Disk] = Device{ - Device: opts.Disk, - - ResetPartitionTable: opts.Force, - Zero: opts.Zero, - - SkipOverlayMountsCheck: skipOverlayMountsCheck, - } - - // Initialize any slices we need. Note that a boot partition is not - // required. - - if manifest.Targets[opts.Disk] == nil { - manifest.Targets[opts.Disk] = []*Target{} - } - - var targets []*Target - - // create GRUB BIOS+UEFI partitions, or only one big EFI partition if not using GRUB - if !uefiOnlyBoot { - targets = append(targets, - EFITarget(opts.Disk, nil), - BIOSTarget(opts.Disk, nil), - BootTarget(opts.Disk, &Target{ - PreserveContents: bootLoaderPresent, - }), - ) - } else { - targets = append(targets, - EFITargetUKI(opts.Disk, &Target{ - PreserveContents: bootLoaderPresent, - }), - ) - } - - targets = append(targets, - MetaTarget(opts.Disk, &Target{ - PreserveContents: bootLoaderPresent, - }), - StateTarget(opts.Disk, &Target{ - PreserveContents: bootLoaderPresent, - FormatOptions: &partition.FormatOptions{ - FileSystemType: partition.FilesystemTypeNone, - }, - }), - EphemeralTarget(opts.Disk, NoFilesystem), - ) - - if !opts.Force { - for _, target := range targets { - target.Force = false - target.Skip = true - } - } - - for _, target := range targets { - if target == nil { - continue - } - - manifest.Targets[target.Device] = append(manifest.Targets[target.Device], target) - } - - return manifest, nil -} - -// Execute partitions and formats all disks in a manifest. -func (m *Manifest) Execute() (err error) { - for dev, targets := range m.Targets { - if err = m.executeOnDevice(m.Devices[dev], targets); err != nil { - return err - } - } - - return nil -} - -// checkMounts verifies that no active mounts in any mount namespace exist for the device. -// -//nolint:gocyclo -func (m *Manifest) checkMounts(device Device) error { - matches, err := filepath.Glob("/proc/*/mountinfo") - if err != nil { - return err - } - - for _, path := range matches { - if err = func() error { - var f *os.File - - f, err = os.Open(path) - if err != nil { - // ignore error in case process got removed - return nil - } - - defer f.Close() //nolint:errcheck - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - fields := strings.Fields(scanner.Text()) - - if len(fields) < 2 { - continue - } - - if !device.SkipOverlayMountsCheck && fields[len(fields)-2] == "overlay" { - //nolint:dupword - // parsing options (last column) in the overlay mount line which looks like: - // 163 70 0:52 /apid / ro,relatime - overlay overlay rw,lowerdir=/opt,upperdir=/var/system/overlays/opt-diff,workdir=/var/system/overlays/opt-workdir - - options := strings.Split(fields[len(fields)-1], ",") - - for _, option := range options { - parts := strings.SplitN(option, "=", 2) - if len(parts) == 2 { - if strings.HasPrefix(parts[1], "/var/") { - return fmt.Errorf("found overlay mount in %q: %s", path, scanner.Text()) - } - } - } - } - - if strings.HasPrefix(fields[len(fields)-2], device.Device) { - return fmt.Errorf("found active mount in %q for %q: %s", path, device.Device, scanner.Text()) - } - } - - return f.Close() - }(); err != nil { - return err - } - } - - return nil -} - -//nolint:gocyclo,cyclop -func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error) { - if err = m.checkMounts(device); err != nil { - return err - } - - if err = m.preserveContents(device, targets); err != nil { - return err - } - - if device.Zero { - if err = partition.Format(device.Device, &partition.FormatOptions{ - FileSystemType: partition.FilesystemTypeNone, - }, m.Printf); err != nil { - return err - } - } - - var bd *blockdevice.BlockDevice - - if bd, err = blockdevice.Open(device.Device, blockdevice.WithExclusiveLock(true)); err != nil { - return err - } - - //nolint:errcheck - defer bd.Close() - - var pt *gpt.GPT - - created := false - - pt, err = bd.PartitionTable() - if err != nil { - if !errors.Is(err, blockdevice.ErrMissingPartitionTable) { - return err - } - - m.Printf("creating new partition table on %s", device.Device) - - gptOpts := []gpt.Option{ - gpt.WithMarkMBRBootable(m.LegacyBIOSSupport), - } - - if m.PartitionOptions != nil { - gptOpts = append(gptOpts, gpt.WithPartitionEntriesStartLBA(m.PartitionOptions.PartitionsOffset)) - } - - pt, err = gpt.New(bd.Device(), gptOpts...) - if err != nil { - return err - } - - m.Printf("logical/physical block size: %d/%d", pt.Header().LBA.LogicalBlockSize, pt.Header().LBA.PhysicalBlockSize) - m.Printf("minimum/optimal I/O size: %d/%d", pt.Header().LBA.MinimalIOSize, pt.Header().LBA.OptimalIOSize) - - if err = pt.Write(); err != nil { - return err - } - - if err = bd.Close(); err != nil { - return err - } - - if bd, err = blockdevice.Open(device.Device, blockdevice.WithExclusiveLock(true)); err != nil { - return err - } - - defer bd.Close() //nolint:errcheck - - created = true - } - - if !created { - if device.ResetPartitionTable { - m.Printf("resetting partition table on %s", device.Device) - - // TODO: how should it work with zero option above? - if err = bd.Reset(); err != nil { - return err - } - } else { - // clean up partitions which are going to be recreated - keepPartitions := map[string]struct{}{} - - for _, target := range targets { - if target.Skip { - keepPartitions[target.Label] = struct{}{} - } - } - - // make sure all partitions to be skipped already exist - missingPartitions := map[string]struct{}{} - - for label := range keepPartitions { - missingPartitions[label] = struct{}{} - } - - for _, part := range pt.Partitions().Items() { - delete(missingPartitions, part.Name) - } - - if len(missingPartitions) > 0 { - return fmt.Errorf("some partitions to be skipped are missing: %v", missingPartitions) - } - - // delete all partitions which are not skipped - for _, part := range pt.Partitions().Items() { - if _, ok := keepPartitions[part.Name]; !ok { - m.Printf("deleting partition %s", part.Name) - - if err = pt.Delete(part); err != nil { - return err - } - } - } - - if err = pt.Write(); err != nil { - return err - } - } - } - - pt, err = bd.PartitionTable() - if err != nil { - return err - } - - for i, target := range targets { - if err = target.partition(pt, i, m.Printf); err != nil { - return fmt.Errorf("failed to partition device: %w", err) - } - } - - if err = pt.Write(); err != nil { - return err - } - - for _, target := range targets { - err = retry.Constant(time.Minute, retry.WithUnits(100*time.Millisecond)).Retry(func() error { - e := target.Format(m.Printf) - if e != nil { - if strings.Contains(e.Error(), "No such file or directory") { - // workaround problem with partition device not being visible immediately after partitioning - return retry.ExpectedError(e) - } - - return e - } - - return nil - }) - if err != nil { - return fmt.Errorf("failed to format device: %w", err) - } - } - - return m.restoreContents(targets) -} - -//nolint:gocyclo -func (m *Manifest) preserveContents(device Device, targets []*Target) (err error) { - anyPreserveContents := false - - for _, target := range targets { - if target.Skip { - continue - } - - if target.PreserveContents { - anyPreserveContents = true - - break - } - } - - if !anyPreserveContents { - // no target to preserve contents, exit early - return nil - } - - var bd *blockdevice.BlockDevice - - if bd, err = blockdevice.Open(device.Device); err != nil { - // failed to open the block device, probably it's damaged? - m.Printf("warning: skipping preserve contents on %q as block device failed: %s", device.Device, err) - - return nil - } - - //nolint:errcheck - defer bd.Close() - - pt, err := bd.PartitionTable() - if err != nil { - m.Printf("warning: skipping preserve contents on %q as partition table failed: %s", device.Device, err) - - return nil - } - - for _, target := range targets { - if target.Skip { - continue - } - - if !target.PreserveContents { - continue - } - - var ( - sourcePart *gpt.Partition - fileSystemType partition.FileSystemType - fnmatchFilters []string - ) - - sources := append([]PreserveSource{ - { - Label: target.Label, - FileSystemType: target.FileSystemType, - }, - }, target.ExtraPreserveSources...) - - for _, source := range sources { - // find matching existing partition table entry - for _, part := range pt.Partitions().Items() { - if part.Name == source.Label { - sourcePart = part - fileSystemType = source.FileSystemType - fnmatchFilters = source.FnmatchFilters - - break - } - } - } - - if sourcePart == nil { - m.Printf("warning: failed to preserve contents of %q on %q, as source partition wasn't found", target.Label, device.Device) - - continue - } - - if err = target.SaveContents(device, sourcePart, fileSystemType, fnmatchFilters); err != nil { - m.Printf("warning: failed to preserve contents of %q on %q: %s", target.Label, device.Device, err) - } - } - - return nil -} - -func (m *Manifest) restoreContents(targets []*Target) error { - for _, target := range targets { - if err := target.RestoreContents(); err != nil { - return fmt.Errorf("error restoring contents for %q: %w", target.Label, err) - } - } - - return nil -} - -// SystemMountpoints returns list of system mountpoints for the manifest. -func (m *Manifest) SystemMountpoints(ctx context.Context, opts ...mount.Option) (*mount.Points, error) { - mountpoints := mount.NewMountPoints() - - for dev := range m.Targets { - mp, err := mount.SystemMountPointsForDevice(ctx, dev, opts...) - if err != nil { - return nil, err - } - - iter := mp.Iter() - for iter.Next() { - mountpoints.Set(iter.Key(), iter.Value()) - } - } - - return mountpoints, nil -} - -func shouldSkipOverlayMountsCheck(mode Mode) (bool, error) { - var skipOverlayMountsCheck bool - - _, err := os.Stat("/.dockerenv") - - switch { - case err == nil: - skipOverlayMountsCheck = true - case os.IsNotExist(err): - skipOverlayMountsCheck = mode.IsImage() - default: - return false, fmt.Errorf("cannot determine if /.dockerenv exists: %w", err) - } - - return skipOverlayMountsCheck, nil -} diff --git a/cmd/installer/pkg/install/manifest_test.go b/cmd/installer/pkg/install/manifest_test.go deleted file mode 100644 index 4a2d53fd1e6..00000000000 --- a/cmd/installer/pkg/install/manifest_test.go +++ /dev/null @@ -1,328 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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 ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/loopback" - "github.com/stretchr/testify/suite" - - "github.com/siderolabs/talos/cmd/installer/pkg/install" - "github.com/siderolabs/talos/internal/pkg/mount" - "github.com/siderolabs/talos/internal/pkg/partition" - "github.com/siderolabs/talos/pkg/machinery/constants" -) - -// Some tests in this package cannot be run under buildkit, as buildkit doesn't propagate partition devices -// like /dev/loopXpY into the sandbox. To run the tests on your local computer, do the following: -// -// go test -exec sudo -v --count 1 ./cmd/installer/pkg/install/ - -type manifestSuite struct { - suite.Suite - - disk *os.File - loopbackDevice *os.File -} - -const ( - diskSize = 4 * 1024 * 1024 * 1024 // 4 GiB - lbaSize = 512 - gptReserved = 67 -) - -func TestManifestSuite(t *testing.T) { - suite.Run(t, new(manifestSuite)) -} - -func (suite *manifestSuite) SetupTest() { - suite.skipIfNotRoot() - - var err error - - suite.disk, err = os.CreateTemp("", "talos") - suite.Require().NoError(err) - - suite.Require().NoError(suite.disk.Truncate(diskSize)) - - suite.loopbackDevice, err = loopback.NextLoopDevice() - suite.Require().NoError(err) - - suite.T().Logf("Using %s", suite.loopbackDevice.Name()) - - suite.Require().NoError(loopback.Loop(suite.loopbackDevice, suite.disk)) - - suite.Require().NoError(loopback.LoopSetReadWrite(suite.loopbackDevice)) - - // set the env vars xfsprogs expects to use Talos STATE partition which is 100Megs - // whereas xfs expects a default minimum size of 300Megs if these are not set. - // Ref: https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/tree/mkfs/xfs_mkfs.c?h=v6.3.0#n2582 - suite.T().Setenv("TEST_DIR", "true") - suite.T().Setenv("TEST_DEV", "true") - suite.T().Setenv("QA_CHECK_FS", "true") -} - -func (suite *manifestSuite) TearDownTest() { - if suite.loopbackDevice != nil { - suite.Assert().NoError(loopback.Unloop(suite.loopbackDevice)) - } - - if suite.disk != nil { - suite.Assert().NoError(os.Remove(suite.disk.Name())) - suite.Assert().NoError(suite.disk.Close()) - } -} - -func (suite *manifestSuite) skipUnderBuildkit() { - hostname, _ := os.Hostname() //nolint:errcheck - - if hostname == "buildkitsandbox" { - suite.T().Skip("test not supported under buildkit as partition devices are not propagated from /dev") - } -} - -func (suite *manifestSuite) skipIfNotRoot() { - if os.Getuid() != 0 { - suite.T().Skip("can't run the test as non-root") - } -} - -func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, current, next string, verifyConfigPersistence, verifyEphemeralPersistence bool) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - bd, err := blockdevice.Open(suite.loopbackDevice.Name()) - suite.Require().NoError(err) - - defer bd.Close() //nolint:errcheck - - table, err := bd.PartitionTable() - suite.Require().NoError(err) - - // verify partition table - - suite.Assert().Len(table.Partitions().Items(), 6) - - part := table.Partitions().Items()[0] - suite.Assert().Equal(partition.EFISystemPartition, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.EFIPartitionLabel, part.Name) - suite.Assert().EqualValues(0, part.Attributes) - suite.Assert().EqualValues(partition.EFISize/lbaSize, part.Length()) - - part = table.Partitions().Items()[1] - suite.Assert().Equal(partition.BIOSBootPartition, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.BIOSGrubPartitionLabel, part.Name) - suite.Assert().EqualValues(4, part.Attributes) - suite.Assert().EqualValues(partition.BIOSGrubSize/lbaSize, part.Length()) - - part = table.Partitions().Items()[2] - suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.BootPartitionLabel, part.Name) - suite.Assert().EqualValues(0, part.Attributes) - suite.Assert().EqualValues(partition.BootSize/lbaSize, part.Length()) - - part = table.Partitions().Items()[3] - suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.MetaPartitionLabel, part.Name) - suite.Assert().EqualValues(0, part.Attributes) - suite.Assert().EqualValues(partition.MetaSize/lbaSize, part.Length()) - - part = table.Partitions().Items()[4] - suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.StatePartitionLabel, part.Name) - suite.Assert().EqualValues(0, part.Attributes) - - suite.Assert().EqualValues(partition.StateSize/lbaSize, part.Length()) - - part = table.Partitions().Items()[5] - suite.Assert().Equal(partition.LinuxFilesystemData, strings.ToUpper(part.Type.String())) - suite.Assert().Equal(constants.EphemeralPartitionLabel, part.Name) - suite.Assert().EqualValues(0, part.Attributes) - suite.Assert().Greater(part.Length(), uint64(1024*1024*1024/lbaSize)) - - suite.Assert().NoError(bd.Close()) - - // query mount points directly for the device - - mountpoints, err := mount.SystemMountPointsForDevice(ctx, suite.loopbackDevice.Name()) - suite.Require().NoError(err) - - suite.Assert().Equal(4, mountpoints.Len()) - - // verify filesystems by mounting and unmounting - - tempDir := suite.T().TempDir() - - mountpoints, err = manifest.SystemMountpoints(ctx) - suite.Require().NoError(err) - - suite.Assert().Equal(4, mountpoints.Len()) - - suite.Require().NoError(mount.PrefixMountTargets(mountpoints, tempDir)) - - err = mount.Mount(mountpoints) - suite.Require().NoError(err) - - defer func() { - suite.Assert().NoError(mount.Unmount(mountpoints)) - }() - - metaPath := fmt.Sprintf("%sp%d", suite.loopbackDevice.Name(), table.Partitions().Items()[3].Number) - - if verifyConfigPersistence { - suite.Assert().FileExists(filepath.Join(tempDir, "system", "state", "config.yaml")) - } - - if verifyEphemeralPersistence { - suite.Assert().FileExists(filepath.Join(tempDir, "var", "content")) - } - - if current != "" { - // verify that current was preserved - suite.Assert().DirExists(filepath.Join(tempDir, "boot", current)) - - suite.Assert().FileExists(filepath.Join(tempDir, "boot", current, "kernel")) - - buf := make([]byte, len(current)) - - f, err := os.Open(metaPath) - suite.Require().NoError(err) - - _, err = io.ReadFull(f, buf) - suite.Require().NoError(err) - - suite.Assert().Equal(current, string(buf)) - - suite.Assert().NoError(f.Close()) - } - - if next != "" { - suite.Assert().NoError(os.MkdirAll(filepath.Join(tempDir, "boot", next), 0o700)) - suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "boot", next, "kernel"), []byte("LINUX!"), 0o660)) - suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "system", "state", "config.yaml"), []byte("#!yaml"), 0o660)) - - buf := []byte(next) - - f, err := os.OpenFile(metaPath, os.O_WRONLY, 0) - suite.Require().NoError(err) - - _, err = f.Write(buf) - suite.Require().NoError(err) - - suite.Assert().NoError(f.Close()) - } - - suite.Assert().NoError(os.WriteFile(filepath.Join(tempDir, "var", "content"), []byte("data"), 0o600)) -} - -func (suite *manifestSuite) TestExecuteManifestClean() { - suite.skipUnderBuildkit() - - manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{ - Disk: suite.loopbackDevice.Name(), - Force: true, - Board: constants.BoardNone, - Printf: suite.T().Logf, - }) - suite.Require().NoError(err) - - // in the tests overlay mounts should be ignored - dev := manifest.Devices[suite.loopbackDevice.Name()] - dev.SkipOverlayMountsCheck = true - manifest.Devices[suite.loopbackDevice.Name()] = dev - - suite.Assert().NoError(manifest.Execute()) - - suite.verifyBlockdevice(manifest, "", "A", false, false) -} - -func (suite *manifestSuite) TestExecuteManifestForce() { - suite.skipUnderBuildkit() - - manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{ - Disk: suite.loopbackDevice.Name(), - Force: true, - Board: constants.BoardNone, - Printf: suite.T().Logf, - }) - suite.Require().NoError(err) - - // in the tests overlay mounts should be ignored - dev := manifest.Devices[suite.loopbackDevice.Name()] - dev.SkipOverlayMountsCheck = true - manifest.Devices[suite.loopbackDevice.Name()] = dev - - suite.Assert().NoError(manifest.Execute()) - - suite.verifyBlockdevice(manifest, "", "A", false, false) - - // reinstall - - manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{ - Disk: suite.loopbackDevice.Name(), - Force: true, - Zero: true, - Board: constants.BoardNone, - Printf: suite.T().Logf, - }) - suite.Require().NoError(err) - - // in the tests overlay mounts should be ignored - dev = manifest.Devices[suite.loopbackDevice.Name()] - dev.SkipOverlayMountsCheck = true - manifest.Devices[suite.loopbackDevice.Name()] = dev - - suite.Assert().NoError(manifest.Execute()) - - suite.verifyBlockdevice(manifest, "A", "B", true, false) -} - -func (suite *manifestSuite) TestExecuteManifestPreserve() { - suite.skipUnderBuildkit() - - manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{ - Disk: suite.loopbackDevice.Name(), - Force: true, - Board: constants.BoardNone, - Printf: suite.T().Logf, - }) - suite.Require().NoError(err) - - // in the tests overlay mounts should be ignored - dev := manifest.Devices[suite.loopbackDevice.Name()] - dev.SkipOverlayMountsCheck = true - manifest.Devices[suite.loopbackDevice.Name()] = dev - - suite.Assert().NoError(manifest.Execute()) - - suite.verifyBlockdevice(manifest, "", "A", false, false) - - // reinstall - - manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{ - Disk: suite.loopbackDevice.Name(), - Force: false, - Board: constants.BoardNone, - Printf: suite.T().Logf, - }) - suite.Require().NoError(err) - - // in the tests overlay mounts should be ignored - dev = manifest.Devices[suite.loopbackDevice.Name()] - dev.SkipOverlayMountsCheck = true - manifest.Devices[suite.loopbackDevice.Name()] = dev - - suite.Assert().NoError(manifest.Execute()) - - suite.verifyBlockdevice(manifest, "A", "B", true, true) -} diff --git a/cmd/installer/pkg/install/target.go b/cmd/installer/pkg/install/target.go deleted file mode 100644 index 63aefeea238..00000000000 --- a/cmd/installer/pkg/install/target.go +++ /dev/null @@ -1,371 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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 ( - "bytes" - "compress/gzip" - "context" - "fmt" - "io" - "log" - "os" - "path/filepath" - - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - "github.com/siderolabs/go-blockdevice/blockdevice/util" - "golang.org/x/sys/unix" - - "github.com/siderolabs/talos/internal/pkg/mount" - "github.com/siderolabs/talos/internal/pkg/partition" - "github.com/siderolabs/talos/pkg/archiver" - "github.com/siderolabs/talos/pkg/machinery/constants" -) - -// Target represents an installation partition. -// -//nolint:maligned -type Target struct { - *partition.FormatOptions - *partition.Options - Device string - - LegacyBIOSBootable bool - - // Preserve contents of the partition with the same label (if it exists). - PreserveContents bool - - // Extra preserved locations (for upgrading from older versions of Talos). - // - // Used only if PreserveContents is true. - ExtraPreserveSources []PreserveSource - - // Skip makes manifest skip any actions with the partition (creating, formatting). - // - // Skipped partitions should exist on the disk by the time manifest execution starts. - Skip bool - - // set during execution - PartitionName string - Contents *bytes.Buffer -} - -// PreserveSource instructs Talos where to look for source files to preserve. -type PreserveSource struct { - Label string - FnmatchFilters []string - FileSystemType partition.FileSystemType -} - -// NoFilesystem preset to override default filesystem type to none. -var NoFilesystem = &Target{ - FormatOptions: &partition.FormatOptions{ - FileSystemType: partition.FilesystemTypeNone, - }, -} - -// ParseTarget parses the target from the label and creates a required target. -func ParseTarget(label, deviceName string) (*Target, error) { - switch label { - case constants.EFIPartitionLabel: - return EFITarget(deviceName, nil), nil - case constants.BIOSGrubPartitionLabel: - return BIOSTarget(deviceName, nil), nil - case constants.BootPartitionLabel: - return BootTarget(deviceName, nil), nil - case constants.MetaPartitionLabel: - return MetaTarget(deviceName, nil), nil - case constants.StatePartitionLabel: - return StateTarget(deviceName, NoFilesystem), nil - case constants.EphemeralPartitionLabel: - return EphemeralTarget(deviceName, NoFilesystem), nil - default: - return nil, fmt.Errorf("label %q is not supported", label) - } -} - -// EFITarget builds the default EFI target. -func EFITarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.EFIPartitionLabel), - Options: partition.NewPartitionOptions(constants.EFIPartitionLabel, false), - Device: device, - } - - return target.enhance(extra) -} - -// EFITargetUKI builds the default EFI UKI target. -func EFITargetUKI(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.EFIPartitionLabel), - Options: partition.NewPartitionOptions(constants.EFIPartitionLabel, true), - Device: device, - } - - return target.enhance(extra) -} - -// BIOSTarget builds the default BIOS target. -func BIOSTarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.BIOSGrubPartitionLabel), - Options: partition.NewPartitionOptions(constants.BIOSGrubPartitionLabel, false), - Device: device, - LegacyBIOSBootable: true, - } - - return target.enhance(extra) -} - -// BootTarget builds the default boot target. -func BootTarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.BootPartitionLabel), - Options: partition.NewPartitionOptions(constants.BootPartitionLabel, false), - Device: device, - } - - return target.enhance(extra) -} - -// MetaTarget builds the default meta target. -func MetaTarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.MetaPartitionLabel), - Options: partition.NewPartitionOptions(constants.MetaPartitionLabel, false), - Device: device, - } - - return target.enhance(extra) -} - -// StateTarget builds the default state target. -func StateTarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.StatePartitionLabel), - Options: partition.NewPartitionOptions(constants.StatePartitionLabel, false), - Device: device, - } - - return target.enhance(extra) -} - -// EphemeralTarget builds the default ephemeral target. -func EphemeralTarget(device string, extra *Target) *Target { - target := &Target{ - FormatOptions: partition.NewFormatOptions(constants.EphemeralPartitionLabel), - Options: partition.NewPartitionOptions(constants.EphemeralPartitionLabel, false), - Device: device, - } - - return target.enhance(extra) -} - -func (t *Target) enhance(extra *Target) *Target { - if extra == nil { - return t - } - - t.PreserveContents = extra.PreserveContents - t.ExtraPreserveSources = extra.ExtraPreserveSources - t.Skip = extra.Skip - - if extra.FormatOptions != nil { - t.FormatOptions.FileSystemType = extra.FormatOptions.FileSystemType - } - - return t -} - -func (t *Target) String() string { - return fmt.Sprintf("%s (%q)", t.PartitionName, t.Label) -} - -// Locate existing partition on the disk. -func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) { - part, err := partition.Locate(pt, t.Label) - if err != nil { - return nil, err - } - - t.PartitionName, err = part.Path() - if err != nil { - return nil, err - } - - return part, nil -} - -// partition creates a new partition on the specified device. -func (t *Target) partition(pt *gpt.GPT, pos int, printf func(string, ...any)) (err error) { - if t.Skip { - part := pt.Partitions().FindByName(t.Label) - if part != nil { - printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length()) - - t.PartitionName, err = part.Path() - if err != nil { - return err - } - } - - return nil - } - - partitionName, err := partition.Partition(pt, pos, t.Device, partition.Options{ - PartitionLabel: t.Label, - PartitionType: t.PartitionType, - Size: t.Size, - LegacyBIOSBootable: t.LegacyBIOSBootable, - }, printf) - if err != nil { - return err - } - - t.PartitionName = partitionName - - return nil -} - -// Format creates a filesystem on the device/partition. -func (t *Target) Format(printf func(string, ...any)) error { - if t.Skip { - return nil - } - - return partition.Format(t.PartitionName, t.FormatOptions, printf) -} - -// GetLabel returns the underlaying partition label. -func (t *Target) GetLabel() string { - return t.Label -} - -func withTemporaryMounted(partPath string, flags uintptr, fileSystemType partition.FileSystemType, label string, f func(mountPath string) error) error { - mountPath := filepath.Join(constants.SystemPath, "mnt") - - mountpoints := mount.NewMountPoints() - - mountpoint := mount.NewMountPoint(partPath, mountPath, fileSystemType, unix.MS_NOATIME|flags, "") - mountpoints.Set(label, mountpoint) - - if err := mount.Mount(mountpoints); err != nil { - return fmt.Errorf("failed to mount %q: %w", partPath, err) - } - - defer func() { - if err := mount.Unmount(mountpoints); err != nil { - log.Printf("failed to unmount: %s", err) - } - }() - - return f(mountPath) -} - -// SaveContents saves contents of partition to the target (in-memory). -func (t *Target) SaveContents(device Device, source *gpt.Partition, fileSystemType partition.FileSystemType, fnmatchFilters []string) error { - partPath, err := util.PartPath(device.Device, int(source.Number)) - if err != nil { - return err - } - - if fileSystemType == partition.FilesystemTypeNone { - err = t.saveRawContents(partPath) - } else { - err = t.saveFilesystemContents(partPath, fileSystemType, fnmatchFilters) - } - - if err != nil { - t.Contents = nil - - return err - } - - log.Printf("preserved contents of %q: %d bytes", t.Label, t.Contents.Len()) - - return nil -} - -func (t *Target) saveRawContents(partPath string) error { - src, err := os.Open(partPath) - if err != nil { - return fmt.Errorf("error opening source partition: %q", err) - } - - defer src.Close() //nolint:errcheck - - t.Contents = bytes.NewBuffer(nil) - - zw := gzip.NewWriter(t.Contents) - defer zw.Close() //nolint:errcheck - - _, err = io.Copy(zw, src) - if err != nil { - return fmt.Errorf("error copying partition %q contents: %w", partPath, err) - } - - return src.Close() -} - -func (t *Target) saveFilesystemContents(partPath string, fileSystemType partition.FileSystemType, fnmatchFilters []string) error { - t.Contents = bytes.NewBuffer(nil) - - return withTemporaryMounted(partPath, unix.MS_RDONLY, fileSystemType, t.Label, func(mountPath string) error { - return archiver.TarGz(context.TODO(), mountPath, t.Contents, archiver.WithFnmatchPatterns(fnmatchFilters...)) - }) -} - -// RestoreContents restores previously saved contents to the disk. -func (t *Target) RestoreContents() error { - if t.Contents == nil { - return nil - } - - var err error - - if t.FileSystemType == partition.FilesystemTypeNone { - err = t.restoreRawContents() - } else { - err = t.restoreFilesystemContents() - } - - t.Contents = nil - - if err != nil { - return err - } - - log.Printf("restored contents of %q", t.Label) - - return nil -} - -func (t *Target) restoreRawContents() error { - dst, err := os.OpenFile(t.PartitionName, os.O_WRONLY, 0) - if err != nil { - return fmt.Errorf("error opening source partition: %q", err) - } - - defer dst.Close() //nolint:errcheck - - zr, err := gzip.NewReader(t.Contents) - if err != nil { - return err - } - - _, err = io.Copy(dst, zr) - if err != nil { - return fmt.Errorf("error restoring partition %q contents: %w", t.PartitionName, err) - } - - return dst.Close() -} - -func (t *Target) restoreFilesystemContents() error { - return withTemporaryMounted(t.PartitionName, 0, t.FileSystemType, t.Label, func(mountPath string) error { - return archiver.UntarGz(context.TODO(), t.Contents, mountPath) - }) -} diff --git a/cmd/installer/pkg/install/target_test.go b/cmd/installer/pkg/install/target_test.go deleted file mode 100644 index 1f4d8650cde..00000000000 --- a/cmd/installer/pkg/install/target_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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/require" - - "github.com/siderolabs/talos/cmd/installer/pkg/install" -) - -func TestParseTarget(t *testing.T) { - type args struct { - label string - deviceName string - } - - tests := map[string]struct { - args args - want *install.Target - wantErr bool - }{ - "EPHEMERAL": { - args: args{ - label: "EPHEMERAL", - deviceName: "/dev/sda", - }, - want: install.EphemeralTarget("/dev/sda", install.NoFilesystem), - }, - "UNKNOWN": { - args: args{ - label: "UNKNOWN", - deviceName: "/dev/sda", - }, - wantErr: true, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - got, err := install.ParseTarget(tt.args.label, tt.args.deviceName) - if (err != nil) != tt.wantErr { - t.Errorf("ParseTarget() error = %v, wantErr %v", err, tt.wantErr) - - return - } - - require.Equal(t, tt.want, got) - }) - } -} diff --git a/cmd/installer/pkg/install/verify.go b/cmd/installer/pkg/install/verify.go deleted file mode 100644 index 48f2f4bdd1b..00000000000 --- a/cmd/installer/pkg/install/verify.go +++ /dev/null @@ -1,65 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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 ( - "errors" - "fmt" - - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/filesystem" - - "github.com/siderolabs/talos/pkg/machinery/constants" -) - -// VerifyEphemeralPartition verifies the supplied data device options. -func VerifyEphemeralPartition(opts *Options) (err error) { - if opts.Disk == "" { - return errors.New("missing disk") - } - - if opts.Force { - return nil - } - - if err = VerifyDiskAvailability(opts.Disk, constants.EphemeralPartitionLabel); err != nil { - return fmt.Errorf("failed to verify disk availability: %w", err) - } - - return nil -} - -// VerifyDiskAvailability verifies that no filesystems currently exist with -// the labels used by the OS. -func VerifyDiskAvailability(devpath, label string) (err error) { - var dev *blockdevice.BlockDevice - - if dev, err = blockdevice.Open(devpath); err != nil { - // We return here because we only care if we can discover the - // device successfully and confirm that the disk is not in use. - // TODO(andrewrynhard): We should return a custom error type here - // that we can use to confirm the device was not found. - return nil - } - - //nolint:errcheck - defer dev.Close() - - part, err := dev.GetPartition(label) - if err != nil { - return err - } - - fsType, err := part.Filesystem() - if err != nil { - return err - } - - if fsType != filesystem.Unknown { - return fmt.Errorf("target install device %s is not empty, found existing %s file system", label, fsType) - } - - return nil -} diff --git a/cmd/talosctl/cmd/mgmt/cluster/create.go b/cmd/talosctl/cmd/mgmt/cluster/create.go index 2ca704a4fd5..382cdb17dc2 100644 --- a/cmd/talosctl/cmd/mgmt/cluster/create.go +++ b/cmd/talosctl/cmd/mgmt/cluster/create.go @@ -24,7 +24,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/go-getter/v2" "github.com/siderolabs/gen/maps" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" + "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-kubeconfig" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" diff --git a/cmd/talosctl/cmd/talos/upgrade.go b/cmd/talosctl/cmd/talos/upgrade.go index df0d4de13ed..745d433c6d2 100644 --- a/cmd/talosctl/cmd/talos/upgrade.go +++ b/cmd/talosctl/cmd/talos/upgrade.go @@ -164,5 +164,10 @@ func init() { 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") upgradeCmdFlags.addTrackActionFlags(upgradeCmd) + + if err := upgradeCmd.Flags().MarkHidden("preserve"); err != nil { + panic(err) + } + addCommand(upgradeCmd) } diff --git a/go.mod b/go.mod index 26a1c2d9e8c..db060f630b5 100644 --- a/go.mod +++ b/go.mod @@ -132,7 +132,7 @@ require ( github.com/siderolabs/gen v0.5.0 github.com/siderolabs/go-api-signature v0.3.4 github.com/siderolabs/go-blockdevice v0.4.7 - github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240805130014-114af2019684 + github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb github.com/siderolabs/go-circular v0.2.0 github.com/siderolabs/go-cmd v0.1.1 github.com/siderolabs/go-copy v0.1.0 @@ -184,6 +184,8 @@ require ( sigs.k8s.io/yaml v1.4.0 ) +require github.com/siderolabs/protoenc v0.2.1 + require ( github.com/0x5a17ed/itkit v0.6.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect @@ -199,6 +201,7 @@ require ( github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/ProtonMail/gopenpgp/v2 v2.7.5 // indirect github.com/adrg/xdg v0.4.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect @@ -253,6 +256,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -313,10 +317,10 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/siderolabs/protoenc v0.2.1 // indirect github.com/siderolabs/tcpproxy v0.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.10.0 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a // indirect github.com/vbatts/tar-split v0.11.3 // indirect diff --git a/go.sum b/go.sum index 68ffcb00859..5b5948acd6e 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/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= @@ -284,6 +286,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -583,8 +587,8 @@ github.com/siderolabs/go-api-signature v0.3.4 h1:bl8qiwhKLVpdzmjzWtKHTvWZyb7Oe4d github.com/siderolabs/go-api-signature v0.3.4/go.mod h1:qp7De5ZrF021aPrhm5MyLPuaRhkiX4BADmZweqChw4I= github.com/siderolabs/go-blockdevice v0.4.7 h1:2bk4WpEEflGxjrNwp57ye24Pr+cYgAiAeNMWiQOuWbQ= github.com/siderolabs/go-blockdevice v0.4.7/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA= -github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240805130014-114af2019684 h1:nyBRi5IO2/rjdj83/7AUWPxx1soG8WWCUOEqMMI+ABE= -github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240805130014-114af2019684/go.mod h1:5GnL7VLNp5/vgiwYP74fi6KuTUfqGcRxQxtto2tzD+I= +github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb h1:zhvZ4cWyaNwJvpMmecvWffM11E6x8ON8O8Dkmb3DO8k= +github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w= github.com/siderolabs/go-circular v0.2.0 h1:Xca8zrjF/YsujLbwDSojkKzJe7ngetnpuIJn8N78DJI= github.com/siderolabs/go-circular v0.2.0/go.mod h1:rrYCwHLYWmxqrmZP+LjYtwB2a55lxzQi0Ztu1VpWZSc= github.com/siderolabs/go-cmd v0.1.1 h1:nTouZUSxLeiiEe7hFexSVvaTsY/3O8k1s08BxPRrsps= @@ -644,6 +648,8 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/hack/structprotogen/proto/proto.go b/hack/structprotogen/proto/proto.go index 8934ae7eb67..89c38a5ff17 100644 --- a/hack/structprotogen/proto/proto.go +++ b/hack/structprotogen/proto/proto.go @@ -394,6 +394,8 @@ func formatTypeName(fieldTypePkg string, fieldType string, declPkg string) (stri 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" default: return "", "" } diff --git a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go index 6da536fa4e5..53393929b6b 100644 --- a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go +++ b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go @@ -35,8 +35,6 @@ import ( "github.com/prometheus/procfs" "github.com/rs/xid" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - bddisk "github.com/siderolabs/go-blockdevice/blockdevice/util/disk" "github.com/siderolabs/go-kmsg" "github.com/siderolabs/go-pointer" "go.etcd.io/etcd/api/v3/etcdserverpb" @@ -50,9 +48,9 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" - installer "github.com/siderolabs/talos/cmd/installer/pkg/install" "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" @@ -64,6 +62,7 @@ import ( "github.com/siderolabs/talos/internal/pkg/install" "github.com/siderolabs/talos/internal/pkg/meta" "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" @@ -83,6 +82,7 @@ import ( machinetype "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" 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" @@ -357,18 +357,22 @@ func (s *Server) Rollback(ctx context.Context, in *machine.RollbackRequest) (*ma return nil, err } - systemDisk := s.Controller.Runtime().State().Machine().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) + } + if systemDisk == nil { return nil, status.Errorf(codes.FailedPrecondition, "system disk not found") } if err := func() error { - config, err := bootloader.Probe(ctx, systemDisk.Device().Name()) + config, err := bootloader.Probe(systemDisk.DevPath, options.ProbeOptions{}) if err != nil { return err } - return config.Revert(ctx) + return config.Revert(systemDisk.DevPath) }(); err != nil { return nil, err } @@ -467,7 +471,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*mach return nil, err } - log.Printf("upgrade request received: preserve %v, staged %v, force %v, reboot mode %v", in.GetPreserve(), in.GetStage(), in.GetForce(), in.GetRebootMode().String()) + 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()) @@ -490,7 +494,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*mach // 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(), in.GetPreserve()); err != nil { + if err = etcdClient.ValidateForUpgrade(ctx, s.Controller.Runtime().Config()); err != nil { return nil, fmt.Errorf("error validating etcd for upgrade: %w", err) } } @@ -553,7 +557,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*mach type ResetOptions struct { *machine.ResetRequest - systemDiskTargets []*installer.Target + systemDiskTargets []*partition.VolumeWipeTarget } // GetSystemDiskTargets implements runtime.ResetOptions interface. @@ -562,7 +566,7 @@ func (opt *ResetOptions) GetSystemDiskTargets() []runtime.PartitionTarget { return nil } - return xslices.Map(opt.systemDiskTargets, func(t *installer.Target) runtime.PartitionTarget { return t }) + return xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) runtime.PartitionTarget { return t }) } // Reset resets the node. @@ -582,18 +586,22 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma return nil, errors.New("reset failed: invalid input, wipe mode SYSTEM_DISK doesn't support UserDisksToWipe parameter") } - var diskList []*bddisk.Disk - - diskList, err = bddisk.List() + diskList, err := safe.StateListAll[*block.Disk](ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { - return nil, err + return nil, fmt.Errorf("listing disks failed: %w", err) } - disks := xslices.ToMap(diskList, func(disk *bddisk.Disk) (string, *bddisk.Disk) { - return disk.DeviceName, disk - }) + 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 := s.Controller.Runtime().State().Machine().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() { @@ -602,11 +610,11 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma return nil, fmt.Errorf("reset user disk failed: device %s wasn't found", deviceName) } - if disk.ReadOnly { + if disk.TypedSpec().Readonly { return nil, fmt.Errorf("reset user disk failed: device %s is readonly", deviceName) } - if systemDisk != nil && deviceName == systemDisk.Device().Name() { + if systemDisk != nil && deviceName == systemDisk.DevPath { return nil, fmt.Errorf("reset user disk failed: device %s is the system disk", deviceName) } } @@ -617,24 +625,18 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma return nil, errors.New("reset failed: invalid input, wipe mode USER_DISKS doesn't support SystemPartitionsToWipe parameter") } - bd := s.Controller.Runtime().State().Machine().Disk().BlockDevice - - var pt *gpt.GPT - - pt, err = bd.PartitionTable() - if err != nil { - return nil, fmt.Errorf("error reading partition table: %w", err) - } - for _, spec := range in.GetSystemPartitionsToWipe() { - target, err := installer.ParseTarget(spec.Label, bd.Device().Name()) + volumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), spec.Label) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get volume status with label %q: %w", spec.Label, err) } - _, err = target.Locate(pt) - if err != nil { - return nil, fmt.Errorf("failed location partition with label %q: %w", spec.Label, err) + if volumeStatus.TypedSpec().Phase != block.VolumePhaseReady { + return nil, fmt.Errorf("failed to reset: volume %q is not ready", spec.Label) + } + + target := &partition.VolumeWipeTarget{ + VolumeStatus: volumeStatus, } if spec.Wipe { diff --git a/internal/app/machined/main.go b/internal/app/machined/main.go index 354b84d66ba..4686802840c 100644 --- a/internal/app/machined/main.go +++ b/internal/app/machined/main.go @@ -14,6 +14,7 @@ import ( "os" "os/signal" "path/filepath" + "sync" "syscall" "time" @@ -179,6 +180,9 @@ func run() error { return err } + var controllerWaitGroup sync.WaitGroup + defer controllerWaitGroup.Wait() // wait for controller-runtime to finish before rebooting + ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -204,8 +208,12 @@ func run() error { } }() + controllerWaitGroup.Add(1) + // Start v2 controller runtime. go func() { + defer controllerWaitGroup.Done() + if e := c.V1Alpha2().Run(ctx, drainer); e != nil { ctrlErr := fmt.Errorf("fatal controller runtime error: %s", e) diff --git a/internal/app/machined/pkg/controllers/block/discovery.go b/internal/app/machined/pkg/controllers/block/discovery.go index 9244411ffd2..802e375e9c8 100644 --- a/internal/app/machined/pkg/controllers/block/discovery.go +++ b/internal/app/machined/pkg/controllers/block/discovery.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "path/filepath" - "strconv" "time" "github.com/cosi-project/runtime/pkg/controller" @@ -17,6 +16,7 @@ import ( "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" @@ -38,6 +38,11 @@ func (ctrl *DiscoveryController) Inputs() []controller.Input { Type: block.DeviceType, Kind: controller.InputWeak, }, + { + Namespace: block.NamespaceName, + Type: block.DiscoveryRefreshRequestType, + Kind: controller.InputWeak, + }, } } @@ -48,18 +53,25 @@ func (ctrl *DiscoveryController) Outputs() []controller.Output { Type: block.DiscoveredVolumeType, Kind: controller.OutputExclusive, }, + { + Type: block.DiscoveryRefreshStatusType, + Kind: controller.OutputExclusive, + }, } } // Run implements controller.Controller interface. // -//nolint:gocyclo +//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{} @@ -86,7 +98,27 @@ func (ctrl *DiscoveryController) Run(ctx context.Context, r controller.Runtime, 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) @@ -169,9 +201,11 @@ func (ctrl *DiscoveryController) rescan(ctx context.Context, r controller.Runtim 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 + dv.TypedSpec().ParentDevPath = filepath.Join("/dev", device.TypedSpec().Parent) dv.TypedSpec().Size = info.Size dv.TypedSpec().SectorSize = info.SectorSize @@ -187,12 +221,14 @@ func (ctrl *DiscoveryController) rescan(ctx context.Context, r controller.Runtim touchedIDs[id] = struct{}{} for _, nested := range info.Parts { - partID := partitionID(id, nested.PartitionIndex) + 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().Size = nested.ProbedSize @@ -287,13 +323,3 @@ func (ctrl *DiscoveryController) fillDiscoveredVolumeFromInfo(dv *block.Discover dv.TypedSpec().FilesystemBlockSize = info.FilesystemBlockSize dv.TypedSpec().ProbedSize = info.ProbedSize } - -func partitionID(devname string, part uint) string { - result := devname - - if len(result) > 0 && result[len(result)-1] >= '0' && result[len(result)-1] <= '9' { - result += "p" - } - - return result + strconv.FormatUint(uint64(part), 10) -} diff --git a/internal/app/machined/pkg/controllers/block/disks.go b/internal/app/machined/pkg/controllers/block/disks.go index 601e89af25d..16d28be2bcb 100644 --- a/internal/app/machined/pkg/controllers/block/disks.go +++ b/internal/app/machined/pkg/controllers/block/disks.go @@ -156,6 +156,7 @@ func (ctrl *DisksController) analyzeBlockDevice(ctx context.Context, r controlle touchedDisks[device.Metadata().ID()] = struct{}{} return safe.WriterModify(ctx, r, block.NewDisk(block.NamespaceName, device.Metadata().ID()), func(d *block.Disk) error { + d.TypedSpec().DevPath = filepath.Join("/dev", device.Metadata().ID()) d.TypedSpec().Size = size d.TypedSpec().IOSize = ioSize d.TypedSpec().SectorSize = sectorSize diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/disk.go b/internal/app/machined/pkg/controllers/block/internal/volumes/disk.go new file mode 100644 index 00000000000..70713afee14 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/disk.go @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" +) + +// 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 +} + +// 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, + } + 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, + } + } + + if info.Name != "gpt" { + // not empty, and not gpt => can't be used for partitioning + return CheckDiskResult{} + } + 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() + + return CheckDiskResult{ + CanProvision: available >= volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize, + HasGPT: true, + DiskSize: info.Size, + } +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/encrypt.go b/internal/app/machined/pkg/controllers/block/internal/volumes/encrypt.go new file mode 100644 index 00000000000..9fe2b3c1600 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/encrypt.go @@ -0,0 +1,109 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + "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" + "github.com/siderolabs/talos/pkg/machinery/resources/hardware" +) + +// HandleEncryption makes sure the encryption for the volumes is handled appropriately. +func HandleEncryption(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { + getSystemInformation := func(ctx context.Context) (*hardware.SystemInformation, error) { + if volumeContext.SystemInformation != nil { + return volumeContext.SystemInformation, nil + } + + return nil, fmt.Errorf("system information not available") + } + + 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 + + return nil + case block.EncryptionProviderLUKS2: + encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption + + handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), getSystemInformation) + 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) + } +} + +// HandleEncryptionWithHandler makes sure the encryption for the volumes is handled appropriately. +func HandleEncryptionWithHandler(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext, handler *encryption.Handler) 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, blockdev.OpenForWrite()) + if err != nil { + return xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) + } + + defer dev.Close() //nolint:errcheck + + if err = dev.TryLock(true); 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 { + case info.Name == "": + // 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 info.Name == "luks": + // already encrypted + default: + // mismatch + return fmt.Errorf("block dev type mismatch: %s != %s", info.Name, "luks") + } + + logger.Info("opening encrypted volume") + + encryptedName := filepath.Base(volumeContext.Status.Location) + "-encrypted" + + encryptedPath, err := handler.Open(ctx, logger, volumeContext.Status.Location, encryptedName) + if err != nil { + return xerrors.NewTaggedf[Retryable]("error opening encrypted volume: %w", err) + } + + volumeContext.Status.Phase = block.VolumePhasePrepared + volumeContext.Status.MountLocation = encryptedPath + volumeContext.Status.EncryptionProvider = volumeContext.Cfg.TypedSpec().Encryption.Provider + + return nil +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/format.go b/internal/app/machined/pkg/controllers/block/internal/volumes/format.go new file mode 100644 index 00000000000..1bec5950e5e --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/format.go @@ -0,0 +1,111 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "fmt" + + "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/pkg/machinery/resources/block" + "github.com/siderolabs/talos/pkg/makefs" +) + +// Format establishes a filesystem on a block device. +// +//nolint:gocyclo +func Format(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, blockdev.OpenForWrite()) + if err != nil { + return xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) + } + + defer dev.Close() //nolint:errcheck + + if err = dev.TryLock(true); 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: + // 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.Filesystem, _ = block.FilesystemTypeString(info.Name) //nolint:errcheck + } else { + volumeContext.Status.Filesystem = block.FilesystemTypeNone + } + + volumeContext.Status.Phase = block.VolumePhaseReady + + return nil + case info.Name == "": + // no filesystem, format + case info.Name == volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type.String(): + // filesystem already exists + 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)) + } + + if err = makefs.XFS(volumeContext.Status.MountLocation, makefsOptions...); err != nil { + return fmt.Errorf("error formatting XFS: %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 +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/grow.go b/internal/app/machined/pkg/controllers/block/internal/volumes/grow.go new file mode 100644 index 00000000000..814192afcc9 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/grow.go @@ -0,0 +1,88 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "fmt" + + "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(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.TryLock(true); 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.Size += availableGrowth + + return nil +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/locate.go b/internal/app/machined/pkg/controllers/block/internal/volumes/locate.go new file mode 100644 index 00000000000..51222ee77ac --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/locate.go @@ -0,0 +1,159 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "fmt" + + "github.com/siderolabs/gen/value" + "github.com/siderolabs/gen/xerrors" + "github.com/siderolabs/go-blockdevice/v2/partitioning" + "go.uber.org/zap" + + "github.com/siderolabs/talos/pkg/machinery/cel/celenv" + "github.com/siderolabs/talos/pkg/machinery/resources/block" +) + +// LocateAndProvision locates and provisions a volume. +// +// It verifies that the volume is either already created, and ready to be used, +// or provisions it. +// +//nolint:gocyclo,cyclop +func LocateAndProvision(logger *zap.Logger, volumeContext ManagerContext) error { + volumeType := volumeContext.Cfg.TypedSpec().Type + + if volumeType == block.VolumeTypeTmpfs { + // tmpfs volumes are always ready + volumeContext.Status.Phase = block.VolumePhaseReady + + return nil + } + + // below for partition/disk volumes: + if value.IsZero(volumeContext.Cfg.TypedSpec().Locator) { + return fmt.Errorf("volume locator is not set") + } + + // attempt to discover the volume + for _, dv := range volumeContext.DiscoveredVolumes { + matches, err := volumeContext.Cfg.TypedSpec().Locator.Match.EvalBool(celenv.VolumeLocator(), map[string]any{ + "volume": dv, + }) + if err != nil { + return fmt.Errorf("error evaluating volume locator: %w", err) + } + + if matches { + volumeContext.Status.Phase = block.VolumePhaseLocated + volumeContext.Status.Location = dv.DevPath + volumeContext.Status.PartitionIndex = int(dv.PartitionIndex) + volumeContext.Status.ParentLocation = dv.ParentDevPath + + volumeContext.Status.UUID = dv.Uuid + volumeContext.Status.PartitionUUID = dv.PartitionUuid + volumeContext.Status.Size = dv.Size + + return nil + } + } + + if !volumeContext.DevicesReady { + // volume wasn't located, but devices are not ready yet + volumeContext.Status.Phase = block.VolumePhaseWaiting + + return nil + } + + // if we got here, the volume is missing, so it needs to be provisioned + if value.IsZero(volumeContext.Cfg.TypedSpec().Provisioning) { + // the volume can't be provisioned, because the provisioning instructions are missing + volumeContext.Status.Phase = block.VolumePhaseMissing + + return nil + } + + if !volumeContext.PreviousWaveProvisioned { + // previous wave is not provisioned yet + volumeContext.Status.Phase = block.VolumePhaseWaiting + + return nil + } + + // locate the disk(s) for the volume + var matchedDisks []string + + for _, diskCtx := range volumeContext.Disks { + if diskCtx.Disk.Readonly { + // skip readonly disks, they can't be provisioned either way + continue + } + + matches, err := volumeContext.Cfg.TypedSpec().Provisioning.DiskSelector.Match.EvalBool(celenv.DiskLocator(), map[string]any{ + "disk": diskCtx.Disk, + "system_disk": diskCtx.SystemDisk, + }) + if err != nil { + return fmt.Errorf("error evaluating disk locator: %w", err) + } + + if matches { + matchedDisks = append(matchedDisks, diskCtx.Disk.DevPath) + } + } + + if len(matchedDisks) == 0 { + return fmt.Errorf("no disks matched for volume") + } + + logger.Debug("matched disks", zap.Strings("disks", matchedDisks)) + + // analyze each disk, until we find the one which is the best fit + var ( + pickedDisk string + diskCheckResult CheckDiskResult + ) + + for _, matchedDisk := range matchedDisks { + diskCheckResult = CheckDiskForProvisioning(logger, matchedDisk, volumeContext.Cfg) + if diskCheckResult.CanProvision { + pickedDisk = matchedDisk + + break + } + } + + if pickedDisk == "" { + return xerrors.NewTaggedf[Retryable]("no disks matched for volume") + } + + logger.Debug("picked disk", zap.String("disk", pickedDisk)) + + switch volumeType { //nolint:exhaustive + case block.VolumeTypeDisk: + // the disk got matched, so we are done here + volumeContext.Status.Phase = block.VolumePhaseProvisioned + volumeContext.Status.Location = pickedDisk + volumeContext.Status.ParentLocation = "" + volumeContext.Status.Size = diskCheckResult.DiskSize + case block.VolumeTypePartition: + // we need to create a partition on the disk + partitionCreateResult, err := CreatePartition(logger, pickedDisk, volumeContext.Cfg, diskCheckResult.HasGPT) + if err != nil { + return fmt.Errorf("error creating partition: %w", err) + } + + volumeContext.Status.Phase = block.VolumePhaseProvisioned + volumeContext.Status.Location = partitioning.DevName(pickedDisk, uint(partitionCreateResult.PartitionIdx)) + volumeContext.Status.PartitionIndex = partitionCreateResult.PartitionIdx + volumeContext.Status.ParentLocation = pickedDisk + volumeContext.Status.PartitionUUID = partitionCreateResult.Partition.PartGUID.String() + volumeContext.Status.Size = partitionCreateResult.Size + default: + panic("unexpected volume type") + } + + return nil +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/partition.go b/internal/app/machined/pkg/controllers/block/internal/volumes/partition.go new file mode 100644 index 00000000000..08a367a7679 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/partition.go @@ -0,0 +1,117 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "fmt" + + "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/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(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.TryLock(true); 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 := volumeCfg.TypedSpec().Provisioning.PartitionSpec.MaxSize + + 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 = partitionDev.FastWipe(); 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 +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/proto.go b/internal/app/machined/pkg/controllers/block/internal/volumes/proto.go new file mode 100644 index 00000000000..610d19099c3 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/proto.go @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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/cosi-project/runtime/pkg/resource" + "github.com/siderolabs/protoenc" + + "github.com/siderolabs/talos/pkg/machinery/proto" +) + +// ResourceSpecToProto converts a resource spec to a proto message. +func ResourceSpecToProto(i resource.Resource, o proto.Message) error { + marshaled, err := protoenc.Marshal(i.Spec()) + if err != nil { + return err + } + + return proto.Unmarshal(marshaled, o) +} diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/volumes.go b/internal/app/machined/pkg/controllers/block/internal/volumes/volumes.go new file mode 100644 index 00000000000..630ce6bc2db --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/internal/volumes/volumes.go @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" + "github.com/siderolabs/talos/pkg/machinery/resources/block" + "github.com/siderolabs/talos/pkg/machinery/resources/hardware" +) + +// CompareVolumeConfigs compares two volume configs in the proposed order of provisioning. +func CompareVolumeConfigs(a, b *block.VolumeConfig) int { + if c := cmp.Compare(a.TypedSpec().Provisioning.Wave, b.TypedSpec().Provisioning.Wave); c != 0 { + return c + } + + return cmpBool(a.TypedSpec().Provisioning.PartitionSpec.Grow, b.TypedSpec().Provisioning.PartitionSpec.Grow) +} + +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 bool +} + +// ManagerContext captures the context of the volume manager. +type ManagerContext struct { + Cfg *block.VolumeConfig + Status *block.VolumeStatusSpec + DiscoveredVolumes []*blockpb.DiscoveredVolumeSpec + Disks []DiskContext + + DevicesReady bool + PreviousWaveProvisioned bool + SystemInformation *hardware.SystemInformation +} diff --git a/internal/app/machined/pkg/controllers/block/system_disk.go b/internal/app/machined/pkg/controllers/block/system_disk.go index af8747dd527..57824826638 100644 --- a/internal/app/machined/pkg/controllers/block/system_disk.go +++ b/internal/app/machined/pkg/controllers/block/system_disk.go @@ -62,13 +62,17 @@ func (ctrl *SystemDiskController) Run(ctx context.Context, r controller.Runtime, return fmt.Errorf("failed to list discovered volumes: %w", err) } - var systemDiskID string + var ( + systemDiskID string + systemDiskPath string + ) for iter := discoveredVolumes.Iterator(); iter.Next(); { volume := iter.Value() if volume.TypedSpec().PartitionLabel == constants.MetaPartitionLabel { systemDiskID = volume.TypedSpec().Parent + systemDiskPath = volume.TypedSpec().ParentDevPath break } @@ -77,6 +81,7 @@ func (ctrl *SystemDiskController) Run(ctx context.Context, r controller.Runtime, 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 { diff --git a/internal/app/machined/pkg/controllers/block/system_disk_test.go b/internal/app/machined/pkg/controllers/block/system_disk_test.go new file mode 100644 index 00000000000..b4d38c61105 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/system_disk_test.go @@ -0,0 +1,54 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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) +} diff --git a/internal/app/machined/pkg/controllers/block/user_disk_config.go b/internal/app/machined/pkg/controllers/block/user_disk_config.go new file mode 100644 index 00000000000..9c1f31202fe --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/user_disk_config.go @@ -0,0 +1,156 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + "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/pkg/partition" + "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/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.V1Alpha1ID), + Kind: controller.InputWeak, + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *UserDiskConfigController) Outputs() []controller.Output { + return []controller.Output{ + { + Type: block.VolumeConfigType, + 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, logger *zap.Logger) error { + for { + select { + case <-r.EventCh(): + case <-ctx.Done(): + return nil + } + + cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID) + if err != nil && !state.IsNotFoundError(err) { + return fmt.Errorf("error fetching machine configuration") + } + + r.StartTrackingOutputs() + + configurationPresent := cfg != nil && cfg.Config().Machine() != nil + + if configurationPresent { + // user disks + for _, disk := range cfg.Config().Machine().Disks() { + device := disk.Device() + + resolvedDevicePath, err := filepath.EvalSymlinks(device) + if err != nil { + return fmt.Errorf("error resolving device path: %w", err) + } + + for idx, part := range disk.Partitions() { + id := fmt.Sprintf("%s-%d", disk.Device(), idx+1) + + 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{ + Wave: block.WaveUserDisks, + 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), + } + + vc.TypedSpec().Mount = block.MountSpec{ + TargetPath: part.MountPoint(), + } + + return nil + }, + ); err != nil { + return fmt.Errorf("error creating user disk volume configuration: %w", err) + } + } + } + } + + 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().Ready = true + + return nil + }, + ); err != nil { + return fmt.Errorf("error creating user disk configuration status: %w", err) + } + } + } +} diff --git a/internal/app/machined/pkg/controllers/block/user_disk_config_test.go b/internal/app/machined/pkg/controllers/block/user_disk_config_test.go new file mode 100644 index 00000000000..e460bbe7669 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/user_disk_config_test.go @@ -0,0 +1,141 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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/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) + }) + } + + ctest.AssertResource(suite, block.UserDiskConfigStatusID, func(r *block.UserDiskConfigStatus, asrt *assert.Assertions) { + asrt.True(r.TypedSpec().Ready) + }) +} diff --git a/internal/app/machined/pkg/controllers/block/volume_config.go b/internal/app/machined/pkg/controllers/block/volume_config.go new file mode 100644 index 00000000000..b704a497d72 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/volume_config.go @@ -0,0 +1,287 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + "encoding/json" + "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-blockdevice/v2/encryption" + "go.uber.org/zap" + + "github.com/siderolabs/talos/internal/pkg/meta" + "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/config" + "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/runtime" +) + +// VolumeConfigController provides volume configuration based on Talos defaults and machine configuration. +type VolumeConfigController struct{} + +// 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.V1Alpha1ID), + Kind: controller.InputWeak, + }, + { + Namespace: runtime.NamespaceName, + Type: runtime.MetaKeyType, + ID: optional.Some(runtime.MetaKeyTagToID(meta.StateEncryptionConfig)), + Kind: controller.InputWeak, + }, + } +} + +// Outputs implements controller.Controller interface. +func (ctrl *VolumeConfigController) Outputs() []controller.Output { + return []controller.Output{ + { + Type: block.VolumeConfigType, + Kind: controller.OutputShared, + }, + } +} + +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']", constants.MetaPartitionLabel), celenv.VolumeLocator())) +} + +func systemDiskMatch() cel.Expression { + return cel.MustExpression(cel.ParseBooleanExpression("system_disk", celenv.DiskLocator())) +} + +func convertEncryption(in cfg.Encryption, out *block.VolumeConfigSpec) error { + if in == nil { + out.Encryption = block.EncryptionSpec{} + + return nil + } + + switch in.Provider() { + case encryption.LUKS2: + out.Encryption.Provider = block.EncryptionProviderLUKS2 + default: + return fmt.Errorf("unsupported encryption provider: %s", 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() + + 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() + default: + return fmt.Errorf("unsupported encryption key type: slot %d", key.Slot()) + } + } + + return nil +} + +// Run implements controller.Controller interface. +// +//nolint:gocyclo +func (ctrl *VolumeConfigController) Run(ctx context.Context, r controller.Runtime, logger *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.V1Alpha1ID) + if err != nil && !state.IsNotFoundError(err) { + return fmt.Errorf("error fetching machine configuration") + } + + // load STATE encryption meta key + encryptionMeta, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.StateEncryptionConfig)) + if err != nil && !state.IsNotFoundError(err) { + return fmt.Errorf("error fetching state encryption meta key") + } + + r.StartTrackingOutputs() + + // META volume discovery, always created unconditionally + if err = safe.WriterModify(ctx, r, + block.NewVolumeConfig(block.NamespaceName, constants.MetaPartitionLabel), + func(vc *block.VolumeConfig) error { + vc.TypedSpec().Type = block.VolumeTypePartition + vc.TypedSpec().Locator = block.LocatorSpec{ + Match: metaMatch(), + } + + return nil + }, + ); err != nil { + return fmt.Errorf("error creating meta volume configuration: %w", err) + } + + // if config is present (v1apha1 part of now) + // [TODO]: support custom configuration later + configurationPresent := cfg != nil && cfg.Config().Machine() != nil + + // STATE configuration should be always created, but it depends on the configuration presence + if err = safe.WriterModify(ctx, r, + block.NewVolumeConfig(block.NamespaceName, constants.StatePartitionLabel), + func(vc *block.VolumeConfig) error { + vc.TypedSpec().Type = block.VolumeTypePartition + vc.TypedSpec().Mount = block.MountSpec{ + TargetPath: constants.StateMountPoint, + } + + if configurationPresent { + vc.TypedSpec().Provisioning = block.ProvisioningSpec{ + Wave: block.WaveSystemDisk, + DiskSelector: block.DiskSelector{ + Match: systemDiskMatch(), + }, + PartitionSpec: block.PartitionSpec{ + MinSize: partition.StateSize, + MaxSize: partition.StateSize, + Label: constants.StatePartitionLabel, + TypeUUID: partition.LinuxFilesystemData, + }, + FilesystemSpec: block.FilesystemSpec{ + Type: block.FilesystemTypeXFS, + Label: constants.StatePartitionLabel, + }, + } + + vc.TypedSpec().Locator = block.LocatorSpec{ + Match: labelVolumeMatch(constants.StatePartitionLabel), + } + + if err = convertEncryption( + cfg.Config().Machine().SystemDiskEncryption().Get(constants.StatePartitionLabel), + vc.TypedSpec(), + ); err != nil { + return fmt.Errorf("error converting encryption for %s: %w", constants.StatePartitionLabel, err) + } + } else { + vc.TypedSpec().Locator = block.LocatorSpec{ + Match: labelVolumeMatchAndNonEmpty(constants.StatePartitionLabel), + } + + if encryptionMeta != nil { + var encryptionFromMeta *v1alpha1.EncryptionConfig + + if err := json.Unmarshal([]byte(encryptionMeta.TypedSpec().Value), &encryptionFromMeta); err != nil { + return fmt.Errorf("error unmarshalling state encryption meta key: %w", err) + } + + if err = convertEncryption( + encryptionFromMeta, + vc.TypedSpec(), + ); err != nil { + return fmt.Errorf("error converting encryption for %s: %w", constants.StatePartitionLabel, err) + } + } else { + vc.TypedSpec().Encryption = block.EncryptionSpec{} + } + } + + return nil + }, + ); err != nil { + return fmt.Errorf("error creating state volume configuration: %w", err) + } + + if configurationPresent { + if err = safe.WriterModify(ctx, r, + block.NewVolumeConfig(block.NamespaceName, constants.EphemeralPartitionLabel), + func(vc *block.VolumeConfig) error { + vc.TypedSpec().Type = block.VolumeTypePartition + + vc.TypedSpec().Provisioning = block.ProvisioningSpec{ + Wave: block.WaveSystemDisk, + DiskSelector: block.DiskSelector{ + Match: systemDiskMatch(), + }, + PartitionSpec: block.PartitionSpec{ + MinSize: partition.EphemeralMinSize, + Grow: true, + Label: constants.EphemeralPartitionLabel, + TypeUUID: partition.LinuxFilesystemData, + }, + FilesystemSpec: block.FilesystemSpec{ + Type: block.FilesystemTypeXFS, + Label: constants.EphemeralPartitionLabel, + }, + } + + vc.TypedSpec().Mount = block.MountSpec{ + TargetPath: constants.EphemeralMountPoint, + } + + vc.TypedSpec().Locator = block.LocatorSpec{ + Match: labelVolumeMatch(constants.EphemeralPartitionLabel), + } + + if err = convertEncryption( + cfg.Config().Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel), + vc.TypedSpec(), + ); err != nil { + return fmt.Errorf("error converting encryption for %s: %w", constants.EphemeralPartitionLabel, err) + } + + return nil + }, + ); err != nil { + return fmt.Errorf("error creating ephemeral volume configuration: %w", err) + } + } + + // [TODO]: this would fail as it doesn't handle finalizers properly + if err = safe.CleanupOutputs[*block.VolumeConfig](ctx, r); err != nil { + return fmt.Errorf("error cleaning up volume configuration: %w", err) + } + } +} diff --git a/internal/app/machined/pkg/controllers/block/volume_config_test.go b/internal/app/machined/pkg/controllers/block/volume_config_test.go new file mode 100644 index 00000000000..f0647c8c771 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/volume_config_test.go @@ -0,0 +1,184 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + "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/internal/pkg/meta" + "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" + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +type VolumeConfigSuite struct { + ctest.DefaultSuite +} + +func TestVolumeConfigSuite(t *testing.T) { + t.Parallel() + + suite.Run(t, &VolumeConfigSuite{ + DefaultSuite: ctest.DefaultSuite{ + Timeout: 3 * time.Second, + AfterSetup: func(suite *ctest.DefaultSuite) { + suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.VolumeConfigController{})) + }, + }, + }) +} + +func (suite *VolumeConfigSuite) TestReconcileDefaults() { + // 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)) + }) + 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) + }) + 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)) + }) + ctest.AssertResource(suite, constants.EphemeralPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { + asrt.NotEmpty(r.TypedSpec().Provisioning) + }) +} + +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([]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) + }) +} diff --git a/internal/app/machined/pkg/controllers/block/volume_manager.go b/internal/app/machined/pkg/controllers/block/volume_manager.go new file mode 100644 index 00000000000..9a8d4100892 --- /dev/null +++ b/internal/app/machined/pkg/controllers/block/volume_manager.go @@ -0,0 +1,390 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + "math" + "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/xerrors" + "github.com/siderolabs/gen/xslices" + "go.uber.org/zap" + + "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" + blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" + "github.com/siderolabs/talos/pkg/machinery/resources/block" + "github.com/siderolabs/talos/pkg/machinery/resources/hardware" + "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +// 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.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, + }, + } +} + +// 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, volumes.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) + } + + systemInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID) + if err != nil && !state.IsNotFoundError(err) { + return fmt.Errorf("error fetching system information: %w", err) + } + + diskSpecs, err := safe.Map(disks, func(d *block.Disk) (volumes.DiskContext, error) { + spec := &blockpb.DiskSpec{} + + if err := volumes.ResourceSpecToProto(d, spec); err != nil { + return volumes.DiskContext{}, err + } + + return volumes.DiskContext{ + Disk: spec, + SystemDisk: systemDisk != nil && d.Metadata().ID() == systemDisk.TypedSpec().DiskID, + }, 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) + } + + volumeConfigIDs := xslices.ToSet(safe.ToSlice(volumeConfigList, func(vc *block.VolumeConfig) resource.ID { return vc.Metadata().ID() })) + + volumeStatuses := xslices.ToMap( + safe.ToSlice(volumeStatusList, func(vs *block.VolumeStatus) *block.VolumeStatus { return vs }), + func(vs *block.VolumeStatus) (resource.ID, *block.VolumeStatusSpec) { + return vs.Metadata().ID(), vs.TypedSpec() + }, + ) + + if volumeStatuses == nil { + volumeStatuses = map[resource.ID]*block.VolumeStatusSpec{} + } + + // ensure all volume configs have our finalizers + for iter := volumeConfigList.Iterator(); iter.Next(); { + vc := iter.Value() + + if vc.Metadata().Phase() != resource.PhaseRunning { + continue + } + + if vc.Metadata().Finalizers().Has(ctrl.Name()) { + continue + } + + if err = r.AddFinalizer(ctx, vc.Metadata(), ctrl.Name()); err != nil { + return fmt.Errorf("error adding finalizer to volume configuration: %w", err) + } + } + + // remove statuses for volume configs that no longer exist + for id := range volumeStatuses { + if _, exists := volumeConfigIDs[id]; !exists { + delete(volumeStatuses, id) + + if err := r.Destroy(ctx, block.NewVolumeStatus(block.NamespaceName, id).Metadata()); err != nil { + return fmt.Errorf("error destroying volume status: %w", err) + } + } + } + + // fill in statuses for volume configs that don't have a status yet + for id := range volumeConfigIDs { + if _, exists := volumeStatuses[id]; !exists { + volumeStatuses[id] = &block.VolumeStatusSpec{ + Phase: block.VolumePhaseWaiting, + } + } + } + + volumeConfigs := safe.ToSlice(volumeConfigList, func(vc *block.VolumeConfig) *block.VolumeConfig { return vc }) + + // re-sort volume configs by provisioning wave + slices.SortStableFunc(volumeConfigs, volumes.CompareVolumeConfigs) + + fullyProvisionedWave := math.MaxInt + + 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())) + + if vc.Metadata().Phase() != resource.PhaseRunning { + // [TODO]: handle me later + continue + } + + prevPhase := volumeStatus.Phase + + if err = ctrl.processVolumeConfig( + ctx, + volumeLogger, + volumes.ManagerContext{ + Cfg: vc, + Status: volumeStatus, + DiscoveredVolumes: discoveredVolumesSpecs, + Disks: diskSpecs, + DevicesReady: devicesReady, + PreviousWaveProvisioned: vc.TypedSpec().Provisioning.Wave <= fullyProvisionedWave, + SystemInformation: systemInfo, + }, + ); err != nil { + volumeStatus.PreFailPhase = volumeStatus.Phase + volumeStatus.Phase = block.VolumePhaseFailed + volumeStatus.ErrorMessage = err.Error() + + if xerrors.TagIs[volumes.Retryable](err) { + shouldRetry = true + } + } else { + volumeStatus.ErrorMessage = "" + volumeStatus.PreFailPhase = block.VolumePhase(0) + } + + if volumeStatus.Phase != block.VolumePhaseReady { + fullyProvisionedWave = vc.TypedSpec().Provisioning.Wave - 1 + } + + if prevPhase != volumeStatus.Phase || err != nil { + volumeLogger.Info("volume status", zap.String("phase", fmt.Sprintf("%s -> %s", prevPhase, volumeStatus.Phase)), zap.Error(err)) + } + } + + // update statuses + for id, spec := range volumeStatuses { + if err = safe.WriterModify(ctx, r, block.NewVolumeStatus(block.NamespaceName, id), func(vs *block.VolumeStatus) error { + *vs.TypedSpec() = *spec + + return nil + }); err != nil { + return fmt.Errorf("error updating volume status: %w", err) + } + } + } +} + +// 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 +func (ctrl *VolumeManagerController) processVolumeConfig(ctx context.Context, logger *zap.Logger, volumeContext volumes.ManagerContext) error { + prevPhase := volumeContext.Status.Phase + + for { + switch volumeContext.Status.Phase { + case block.VolumePhaseReady: + // nothing to do, ready + return nil + case block.VolumePhaseWaiting, block.VolumePhaseMissing: + if err := volumes.LocateAndProvision(logger, volumeContext); err != nil { + return err + } + case block.VolumePhaseLocated: + // grow the partition if needed + if err := volumes.Grow(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(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 + } + + 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 + } +} diff --git a/internal/app/machined/pkg/controllers/cri/runc_memfd_bind.go b/internal/app/machined/pkg/controllers/cri/runc_memfd_bind.go index 89451282a20..5b848efcec5 100644 --- a/internal/app/machined/pkg/controllers/cri/runc_memfd_bind.go +++ b/internal/app/machined/pkg/controllers/cri/runc_memfd_bind.go @@ -148,10 +148,10 @@ func cleanup(path string, logger *zap.Logger) error { // Keep umounting until we hit a umount error. for unix.Unmount(fdPath, unix.MNT_DETACH) == nil { // loop... - logger.Info(fmt.Sprintf("memfd-bind: path %q unmount succeeded...", path)) + logger.Debug(fmt.Sprintf("memfd-bind: path %q unmount succeeded...", path)) } - logger.Info(fmt.Sprintf("memfd-bind: path %q has been cleared of all old bind-mounts", path)) + logger.Debug(fmt.Sprintf("memfd-bind: path %q has been cleared of all old bind-mounts", path)) return nil } diff --git a/internal/app/machined/pkg/runtime/sequencer.go b/internal/app/machined/pkg/runtime/sequencer.go index 9288fb06eff..1e44fea309b 100644 --- a/internal/app/machined/pkg/runtime/sequencer.go +++ b/internal/app/machined/pkg/runtime/sequencer.go @@ -5,6 +5,7 @@ package runtime import ( + "context" "fmt" "github.com/siderolabs/talos/pkg/machinery/api/machine" @@ -135,8 +136,7 @@ type ResetOptions interface { // PartitionTarget provides interface to the disk partition. type PartitionTarget interface { - fmt.Stringer - Format(func(string, ...any)) error + Wipe(context.Context, func(string, ...any)) error GetLabel() string } diff --git a/internal/app/machined/pkg/runtime/state.go b/internal/app/machined/pkg/runtime/state.go index 3d889130ed5..ac992afb428 100644 --- a/internal/app/machined/pkg/runtime/state.go +++ b/internal/app/machined/pkg/runtime/state.go @@ -9,9 +9,7 @@ import ( "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/registry" - "github.com/siderolabs/go-blockdevice/blockdevice/probe" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/disk" configcore "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" ) @@ -32,8 +30,6 @@ type Machine interface { // MachineState defines the machined state. type MachineState interface { - Disk(options ...disk.Option) *probe.ProbedBlockDevice - Close() error Installed() bool IsInstallStaged() bool StagedInstallImageRef() string diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go index 4702a7fad18..5130ea11495 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go @@ -6,33 +6,34 @@ package bootloader import ( - "context" "os" "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/imager/quirks" ) // Bootloader describes a bootloader. type Bootloader interface { - // Install installs the bootloader - Install(options options.InstallOptions) error + // Install the bootloader. + // + // Install mounts the partitions as required. + Install(options options.InstallOptions) (*options.InstallResult, error) // Revert reverts the bootloader entry to the previous state. - Revert(ctx context.Context) error - // PreviousLabel returns the previous bootloader label. - PreviousLabel() string - // UEFIBoot returns true if the bootloader is UEFI-only. - UEFIBoot() bool + // + // Revert mounts the partitions as required. + Revert(disk string) error + // RequiredPartitions returns the required partitions for the bootloader. + RequiredPartitions() []partition.Options } // Probe checks if any supported bootloaders are installed. // -// If 'disk' is empty, it will probe all disks. // Returns nil if it cannot detect any supported bootloader. -func Probe(ctx context.Context, disk string) (Bootloader, error) { - grubBootloader, err := grub.Probe(ctx, disk) +func Probe(disk string, options options.ProbeOptions) (Bootloader, error) { + grubBootloader, err := grub.Probe(disk, options) if err != nil { return nil, err } @@ -41,7 +42,7 @@ func Probe(ctx context.Context, disk string) (Bootloader, error) { return grubBootloader, nil } - sdbootBootloader, err := sdboot.Probe(ctx, disk) + sdbootBootloader, err := sdboot.Probe(disk, options) if err != nil { return nil, err } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/boot_label.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/boot_label.go index 9a44bdd4c9a..4973250b375 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/boot_label.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/boot_label.go @@ -42,11 +42,6 @@ func (c *Config) flip() error { return nil } -// PreviousLabel returns the previous bootloader label. -func (c *Config) PreviousLabel() string { - return string(c.Fallback) -} - // ParseBootLabel parses the given human-readable boot label to a BootLabel. func ParseBootLabel(name string) (BootLabel, error) { switch { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go index e0f877bf5b1..8ff1e4f25ce 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go @@ -10,6 +10,7 @@ import ( "fmt" "path/filepath" + "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" ) @@ -43,10 +44,13 @@ func NewConfig() *Config { } } -// UEFIBoot returns true if bootloader is UEFI-only. -func (c *Config) UEFIBoot() bool { - // grub supports BIOS boot, so false here. - return false +// RequiredPartitions returns the list of partitions required by the bootloader. +func (c *Config) RequiredPartitions() []partition.Options { + return []partition.Options{ + partition.NewPartitionOptions(constants.EFIPartitionLabel, false), + partition.NewPartitionOptions(constants.BIOSGrubPartitionLabel, false), + partition.NewPartitionOptions(constants.BootPartitionLabel, false), + } } // Put puts a new menu entry to the grub config (nothing is written to disk). diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go index dedcc66ba83..3a1ccc86668 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go @@ -10,10 +10,12 @@ import ( "runtime" "strings" - "github.com/siderolabs/go-blockdevice/blockdevice" + "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-cmd/pkg/cmd" + "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/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" ) @@ -24,62 +26,92 @@ const ( ) // Install validates the grub configuration and writes it to the disk. -// +func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, error) { + var installResult *options.InstallResult + + err := mount.PartitionOp( + opts.BootDisk, + []mount.Spec{ + { + PartitionLabel: constants.BootPartitionLabel, + FilesystemType: partition.FilesystemTypeXFS, + MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint), + }, + { + PartitionLabel: constants.EFIPartitionLabel, + FilesystemType: partition.FilesystemTypeVFAT, + MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint), + }, + }, + func() error { + var installErr error + + installResult, installErr = c.install(opts) + + return installErr + }, + []blkid.ProbeOption{ + // installation happens with locked blockdevice + blkid.WithSkipLocking(true), + }, + nil, + nil, + opts.BlkidInfo, + ) + + return installResult, err +} + //nolint:gocyclo -func (c *Config) Install(options options.InstallOptions) error { +func (c *Config) install(opts options.InstallOptions) (*options.InstallResult, error) { if err := c.flip(); err != nil { - return err + return nil, err } if err := utils.CopyFiles( - options.Printf, + opts.Printf, utils.SourceDestination( - options.BootAssets.KernelPath, - filepath.Join(options.MountPrefix, constants.BootMountPoint, string(c.Default), constants.KernelAsset), + opts.BootAssets.KernelPath, + filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.KernelAsset), ), utils.SourceDestination( - options.BootAssets.InitramfsPath, - filepath.Join(options.MountPrefix, constants.BootMountPoint, string(c.Default), constants.InitramfsAsset), + opts.BootAssets.InitramfsPath, + filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.InitramfsAsset), ), ); err != nil { - return err - } - - if err := c.Put(c.Default, options.Cmdline, options.Version); err != nil { - return err + return nil, err } - if err := c.Write(filepath.Join(options.MountPrefix, ConfigPath), options.Printf); err != nil { - return err + if err := c.Put(c.Default, opts.Cmdline, opts.Version); err != nil { + return nil, err } - blk, err := getBlockDeviceName(options.BootDisk) - if err != nil { - return err + if err := c.Write(filepath.Join(opts.MountPrefix, ConfigPath), opts.Printf); err != nil { + return nil, err } var platforms []string - switch options.Arch { + switch opts.Arch { case amd64: platforms = []string{"x86_64-efi", "i386-pc"} case arm64: platforms = []string{"arm64-efi"} } - if runtime.GOARCH == amd64 && options.Arch == amd64 && !options.ImageMode { + if runtime.GOARCH == amd64 && opts.Arch == amd64 && !opts.ImageMode { // let grub choose the platform automatically if not building an image platforms = []string{""} } for _, platform := range platforms { args := []string{ - "--boot-directory=" + filepath.Join(options.MountPrefix, constants.BootMountPoint), - "--efi-directory=" + filepath.Join(options.MountPrefix, constants.EFIMountPoint), + "--boot-directory=" + filepath.Join(opts.MountPrefix, constants.BootMountPoint), + "--efi-directory=" + filepath.Join(opts.MountPrefix, constants.EFIMountPoint), "--removable", } - if options.ImageMode { + if opts.ImageMode { args = append(args, "--no-nvram") } @@ -87,34 +119,22 @@ func (c *Config) Install(options options.InstallOptions) error { args = append(args, "--target="+platform) } - args = append(args, blk) + args = append(args, opts.BootDisk) - options.Printf("executing: grub-install %s", strings.Join(args, " ")) + opts.Printf("executing: grub-install %s", strings.Join(args, " ")) if _, err := cmd.Run("grub-install", args...); err != nil { - return fmt.Errorf("failed to install grub: %w", err) + return nil, fmt.Errorf("failed to install grub: %w", err) } } - return nil -} - -func getBlockDeviceName(bootDisk string) (string, error) { - dev, err := blockdevice.Open(bootDisk, blockdevice.WithMode(blockdevice.ReadonlyMode)) - if err != nil { - return "", err - } - - //nolint:errcheck - defer dev.Close() - - // verify that BootDisk has boot partition - _, err = dev.GetPartition(constants.BootPartitionLabel) - if err != nil { - return "", err + if opts.ExtraInstallStep != nil { + if err := opts.ExtraInstallStep(); err != nil { + return nil, err + } } - blk := dev.Device().Name() - - return blk, nil + return &options.InstallResult{ + PreviousLabel: string(c.Fallback), + }, nil } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go index 1450f19303c..495cdf6a40f 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go @@ -6,30 +6,63 @@ package grub import ( - "context" + "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" + mountv2 "github.com/siderolabs/talos/internal/pkg/mount/v2" + "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" ) -// Probe probes a block device for GRUB bootloader. -// -// If the 'disk' is passed, search happens on that disk only, otherwise searches all partitions. -func Probe(ctx context.Context, disk string) (*Config, error) { +// 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(ctx, disk, constants.BootPartitionLabel, func() error { - var err error + 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) + } - grubConf, err = Read(ConfigPath) - if err != nil { - return err + return nil + }, + options.BlockProbeOptions, + []mountv2.NewPointOption{ + mountv2.WithReadonly(), + }, + []mountv2.OperationOption{ + mountv2.WithSkipIfMounted(), + }, + nil, + ); err != nil { + if xerrors.TagIs[mount.NotFoundTag](err) { + // if partitions are not found, it means GRUB is not installed + return nil, nil } - return nil - }); err != 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) +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go index 5a2cbae66fd..30ebaa55c12 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go @@ -6,68 +6,56 @@ package grub import ( - "context" "errors" "fmt" "log" "os" "path/filepath" - "github.com/siderolabs/go-blockdevice/blockdevice/probe" + "github.com/siderolabs/gen/xerrors" - "github.com/siderolabs/talos/internal/pkg/mount" + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" + mountv2 "github.com/siderolabs/talos/internal/pkg/mount/v2" + "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Revert reverts the bootloader to the previous version. -// -//nolint:gocyclo -func (c *Config) Revert(ctx context.Context) error { +func (c *Config) Revert(disk string) error { if c == nil { return fmt.Errorf("cannot revert bootloader: %w", bootloaderNotInstalledError{}) } - if err := c.flip(); err != nil { + err := mount.PartitionOp( + disk, + []mount.Spec{ + { + PartitionLabel: constants.BootPartitionLabel, + FilesystemType: partition.FilesystemTypeXFS, + MountTarget: constants.BootMountPoint, + }, + }, + c.revert, + nil, + nil, + []mountv2.OperationOption{ + mountv2.WithSkipIfMounted(), + }, + nil, + ) + if err != nil && !xerrors.TagIs[mount.NotFoundTag](err) { return err } - // attempt to probe BOOT partition directly - dev, err := probe.GetDevWithPartitionName(constants.BootPartitionLabel) - if os.IsNotExist(err) { - // no BOOT partition, nothing to revert - return nil - } - - if err != nil { - return err - } - - defer dev.Close() //nolint:errcheck - - mp, err := mount.SystemMountPointForLabel(ctx, dev.BlockDevice, constants.BootPartitionLabel) - if err != nil { - return err - } - - // if no BOOT partition nothing to revert - if mp == nil { - return nil - } + return nil +} - alreadyMounted, err := mp.IsMounted() - if err != nil { +func (c *Config) revert() error { + if err := c.flip(); err != nil { return err } - if !alreadyMounted { - if err = mp.Mount(); err != nil { - return err - } - - defer mp.Unmount() //nolint:errcheck - } - - if _, err = os.Stat(filepath.Join(constants.BootMountPoint, string(c.Default))); errors.Is(err, os.ErrNotExist) { + 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", "") } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount/mount.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount/mount.go index e6dfe3fda30..795030fad60 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount/mount.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount/mount.go @@ -6,102 +6,82 @@ package mount import ( - "context" - "errors" "fmt" - "os" - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/probe" + "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" + "github.com/siderolabs/talos/internal/pkg/mount/v2" ) -// PartitionOp mounts a partition with the specified label, executes the operation func, and unmounts the partition. -// -//nolint:gocyclo -func PartitionOp(ctx context.Context, disk string, partitionLabel string, opFunc func() error) error { - var probedBlockDevice *blockdevice.BlockDevice +// Spec specifies what has to be mounted. +type Spec struct { + PartitionLabel string - switch { - case disk != "": - dev, err := blockdevice.Open(disk, blockdevice.WithMode(blockdevice.ReadonlyMode)) - if err != nil { - return err - } - - defer dev.Close() //nolint:errcheck - - _, err = dev.GetPartition(partitionLabel) - if err != nil { - if errors.Is(err, blockdevice.ErrMissingPartitionTable) || os.IsNotExist(err) { - return nil - } - - return err - } + FilesystemType string - probedBlockDevice = dev - case disk == "": - // attempt to probe partition with partitionLabel on any disk - dev, err := probe.GetDevWithPartitionName(partitionLabel) - if os.IsNotExist(err) { - // no EFI partition, nothing to do - return nil - } + 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, + newPointOptions []mount.NewPointOption, + mountOptions []mount.OperationOption, + info *blkid.Info, // might be nil +) error { + if info == nil { + var err error + + info, err = blkid.ProbePath(disk, probeOptions...) if err != nil { - return err + return fmt.Errorf("error probing disk %s: %w", disk, err) } - - defer dev.Close() //nolint:errcheck - - probedBlockDevice = dev.BlockDevice } - mp, err := mount.SystemMountPointForLabel(ctx, probedBlockDevice, partitionLabel, mount.WithFlags(mount.ReadOnly)) - if err != nil { - return err - } + var points mount.Points - // no mountpoint defined for this partition, should not happen - if mp == nil { - return fmt.Errorf("no mountpoint defined for %s", partitionLabel) - } + for _, spec := range specs { + var found bool - alreadyMounted, err := mp.IsMounted() - if err != nil { - return err - } + 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) + } - if !alreadyMounted { - if err = mp.Mount(); err != nil { - return err - } + points = append(points, + mount.NewPoint( + partitioning.DevName(disk, partition.PartitionIndex), + spec.MountTarget, + spec.FilesystemType, + newPointOptions..., + ), + ) - defer mp.Unmount() //nolint:errcheck - } + found = true - return opFunc() -} + break + } + } -// GetBlockDeviceName returns the block device name for the specified boot disk and partition label. -func GetBlockDeviceName(bootDisk, partitionLabel string) (string, error) { - dev, err := blockdevice.Open(bootDisk, blockdevice.WithMode(blockdevice.ReadonlyMode)) - if err != nil { - return "", err + if !found { + return xerrors.NewTaggedf[NotFoundTag]("partition with label %s not found", spec.PartitionLabel) + } } - //nolint:errcheck - defer dev.Close() - - // verify that BootDisk has partition with the specified label - _, err = dev.GetPartition(partitionLabel) + unmounter, err := points.Mount(mountOptions...) if err != nil { - return "", err + return fmt.Errorf("error mounting partitions: %w", err) } - blk := dev.Device().Name() + defer unmounter() //nolint:errcheck - return blk, nil + return opFunc() } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go index 4ff9719d896..41a5de4ea94 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go @@ -8,6 +8,8 @@ package options import ( "fmt" + "github.com/siderolabs/go-blockdevice/v2/blkid" + "github.com/siderolabs/talos/pkg/machinery/constants" ) @@ -31,8 +33,20 @@ type InstallOptions struct { // 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. @@ -80,3 +94,8 @@ func (assets *BootAssets) FillDefaults(arch string) { } } } + +// ProbeOptions configures bootloader probing. +type ProbeOptions struct { + BlockProbeOptions []blkid.ProbeOption +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go index 1dc3788f558..06aab6e281b 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go @@ -6,7 +6,6 @@ package sdboot import ( - "context" "errors" "fmt" "log" @@ -15,9 +14,13 @@ import ( "strings" "github.com/ecks/uefi/efi/efivario" + "github.com/siderolabs/gen/xerrors" + "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" + mountv2 "github.com/siderolabs/talos/internal/pkg/mount/v2" + "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" ) @@ -52,7 +55,7 @@ func New() *Config { // Probe for existing sd-boot bootloader. // //nolint:gocyclo -func Probe(ctx context.Context, disk string) (*Config, error) { +func Probe(disk string, options options.ProbeOptions) (*Config, error) { // if not UEFI boot, nothing to do if !isUEFIBoot() { return nil, nil @@ -64,19 +67,41 @@ func Probe(ctx context.Context, disk string) (*Config, error) { // 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(ctx, disk, constants.EFIPartitionLabel, 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 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 %q", filepath.Join(constants.EFIMountPoint, "EFI", "boot")) + if len(files) == 0 { + return fmt.Errorf("no boot*.efi files found in %q", filepath.Join(constants.EFIMountPoint, "EFI", "boot")) + } + + return nil + }, + options.BlockProbeOptions, + []mountv2.NewPointOption{ + mountv2.WithReadonly(), + }, + []mountv2.OperationOption{ + mountv2.WithSkipIfMounted(), + }, + nil, + ); err != nil { + if xerrors.TagIs[mount.NotFoundTag](err) { + return nil, nil } - return nil - }); err != nil { return nil, err } @@ -94,21 +119,39 @@ func Probe(ctx context.Context, disk string) (*Config, error) { log.Printf("booted entry: %q", bootedEntry) - if opErr := mount.PartitionOp(ctx, disk, constants.EFIPartitionLabel, func() error { - // list existing UKIs, and check if the current one is present - files, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) - if err != nil { - return err - } + if opErr := mount.PartitionOp( + disk, + []mount.Spec{ + { + PartitionLabel: constants.EFIPartitionLabel, + FilesystemType: partition.FilesystemTypeVFAT, + MountTarget: constants.EFIMountPoint, + }, + }, + func() error { + // list existing UKIs, and check if the current one is present + 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), bootedEntry) { - return nil + for _, file := range files { + if strings.EqualFold(filepath.Base(file), bootedEntry) { + return nil + } } - } - return fmt.Errorf("booted entry %q not found", bootedEntry) - }); opErr != nil { + return fmt.Errorf("booted entry %q not found", bootedEntry) + }, + options.BlockProbeOptions, + []mountv2.NewPointOption{ + mountv2.WithReadonly(), + }, + []mountv2.OperationOption{ + mountv2.WithSkipIfMounted(), + }, + nil, + ); opErr != nil { return nil, opErr } @@ -117,9 +160,43 @@ func Probe(ctx context.Context, disk string) (*Config, error) { }, nil } -// UEFIBoot returns true if bootloader is UEFI-only. -func (c *Config) UEFIBoot() bool { - return true +// RequiredPartitions returns the list of partitions required by the bootloader. +func (c *Config) RequiredPartitions() []partition.Options { + return []partition.Options{ + partition.NewPartitionOptions(constants.EFIPartitionLabel, true), + } +} + +// Install the bootloader. +func (c *Config) Install(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 { + var installErr error + + installResult, installErr = c.install(opts) + + return installErr + }, + []blkid.ProbeOption{ + // installation happens with locked blockdevice + blkid.WithSkipLocking(true), + }, + nil, + nil, + opts.BlkidInfo, + ) + + return installResult, err } // Install the bootloader. @@ -128,26 +205,26 @@ func (c *Config) UEFIBoot() bool { // Writes down the UKI and updates the EFI variables. // //nolint:gocyclo -func (c *Config) Install(options options.InstallOptions) error { +func (c *Config) install(opts options.InstallOptions) (*options.InstallResult, error) { var sdbootFilename string - switch options.Arch { + switch opts.Arch { case "amd64": sdbootFilename = "BOOTX64.efi" case "arm64": sdbootFilename = "BOOTAA64.efi" default: - return fmt.Errorf("unsupported architecture: %s", options.Arch) + return nil, fmt.Errorf("unsupported architecture: %s", opts.Arch) } // list existing UKIs, and clean up all but the current one (used to boot) - files, err := filepath.Glob(filepath.Join(options.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) + files, err := filepath.Glob(filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) if err != nil { - return err + return nil, err } // writing UKI by version-based filename here - ukiPath := fmt.Sprintf("%s-%s.efi", "Talos", options.Version) + ukiPath := fmt.Sprintf("%s-%s.efi", "Talos", opts.Version) for _, file := range files { if strings.EqualFold(filepath.Base(file), c.Default) { @@ -159,75 +236,97 @@ func (c *Config) Install(options options.InstallOptions) error { continue } - options.Printf("removing old UKI: %s", file) + opts.Printf("removing old UKI: %s", file) if err = os.Remove(file); err != nil { - return err + return nil, err } } if err := utils.CopyFiles( - options.Printf, + opts.Printf, utils.SourceDestination( - options.BootAssets.UKIPath, - filepath.Join(options.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", ukiPath), + opts.BootAssets.UKIPath, + filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", ukiPath), ), utils.SourceDestination( - options.BootAssets.SDBootPath, - filepath.Join(options.MountPrefix, constants.EFIMountPoint, "EFI", "boot", sdbootFilename), + opts.BootAssets.SDBootPath, + filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "EFI", "boot", sdbootFilename), ), ); err != nil { - return err + return nil, err } // don't update EFI variables if we're installing to a loop device - if !options.ImageMode { - options.Printf("updating EFI variables") + if !opts.ImageMode { + opts.Printf("updating EFI variables") efiCtx := efivario.NewDefaultContext() // set the new entry as a default one if err := WriteVariable(efiCtx, LoaderEntryDefaultName, ukiPath); err != nil { - return err + return nil, err } // set default 5 second boot timeout if err := WriteVariable(efiCtx, LoaderConfigTimeoutName, "5"); err != nil { - return err + return nil, err } } - return nil -} + if opts.ExtraInstallStep != nil { + if err := opts.ExtraInstallStep(); err != nil { + return nil, err + } + } -// PreviousLabel returns the label of the previous bootloader version. -func (c *Config) PreviousLabel() string { - return c.Fallback + return &options.InstallResult{ + PreviousLabel: c.Fallback, + }, nil } // Revert the bootloader to the previous version. -func (c *Config) Revert(ctx context.Context) error { - if err := mount.PartitionOp(ctx, "", constants.EFIPartitionLabel, func() error { - // use c.Default as the current entry, list other UKIs, find the one which is not c.Default, and update EFI var - files, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) - if err != nil { - return err - } +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, + nil, + []mountv2.OperationOption{ + mountv2.WithSkipIfMounted(), + }, + nil, + ) + if err != nil && !xerrors.TagIs[mount.NotFoundTag](err) { + return err + } - for _, file := range files { - if strings.EqualFold(filepath.Base(file), c.Default) { - continue - } + return nil +} - log.Printf("reverting to previous UKI: %s", file) +func (c *Config) revert() error { + // use c.Default as the current entry, list other UKIs, find the one which is not c.Default, and update EFI var + files, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) + if err != nil { + return err + } - return WriteVariable(efivario.NewDefaultContext(), LoaderEntryDefaultName, filepath.Base(file)) + for _, file := range files { + if strings.EqualFold(filepath.Base(file), c.Default) { + continue } - return errors.New("previous UKI not found") - }); err != nil { - return err + log.Printf("reverting to previous UKI: %s", file) + + return WriteVariable(efivario.NewDefaultContext(), LoaderEntryDefaultName, filepath.Base(file)) } - return nil + return errors.New("previous UKI not found") } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller_test.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller_test.go index db6a8508f31..a04a069ddb5 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller_test.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller_test.go @@ -15,6 +15,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -90,8 +91,6 @@ func (m *mockSequencer) trackCall(name string, doneCh chan struct{}) func(runtim } func TestRun(t *testing.T) { - require := require.New(t) - tests := []struct { name string from runtime.Sequence @@ -138,6 +137,9 @@ func TestRun(t *testing.T) { 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") @@ -175,7 +177,13 @@ func TestRun(t *testing.T) { defer cancel() eg.Go(func() error { - return controller.Run(ctx, tt.from, tt.dataFrom) + 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 { @@ -185,7 +193,13 @@ func TestRun(t *testing.T) { return fmt.Errorf("timed out waiting for %s sequence to start", tt.from.String()) } - return controller.Run(ctx, tt.to, tt.dataTo) + 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) @@ -197,7 +211,7 @@ func TestRun(t *testing.T) { sequencer.callsMu.Lock() defer sequencer.callsMu.Unlock() - require.Equal(1, sequencer.calls[tt.to]) + assert.Equal(1, sequencer.calls[tt.to]) }) } } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go index 00beac3ebd1..aaae98fcdc9 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go @@ -136,7 +136,7 @@ func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase { return r.State().Machine().Installed() }, "mountSystem", - MountStatePartition, + MountStatePartition(false), ).Append( "config", LoadConfig, @@ -178,7 +178,7 @@ func (*Sequencer) Install(r runtime.Runtime) []runtime.Phase { SaveStateEncryptionConfig, ).Append( "mountState", - MountStatePartition, + MountStatePartition(true), ).Append( "saveConfig", SaveConfig, @@ -214,7 +214,7 @@ func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase { ).AppendWhen( r.State().Platform().Mode() != runtime.ModeContainer, "mountState", - MountStatePartition, + MountStatePartition(true), ).Append( "saveConfig", SaveConfig, @@ -413,10 +413,6 @@ func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) ).Append( "dbus", StopDBus, - ).AppendWhen( - !in.GetPreserve() && (r.Config().Machine().Type() != machine.TypeWorker), - "leave", - LeaveEtcd, ).AppendList( stopAllPhaselist(r, in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT), ).Append( @@ -437,9 +433,6 @@ func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, in *machineapi.UpgradeRe return nil default: phases = phases.Append( - "verifyDisk", - VerifyDiskAvailability, - ).Append( "upgrade", Upgrade, ).Append( @@ -473,21 +466,12 @@ func (*Sequencer) Upgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []ru !r.Config().Machine().Kubelet().SkipNodeRegistration(), "drain", CordonAndDrainNode, - ).AppendWhen( - !in.GetPreserve(), - "cleanup", - RemoveAllPods, - ).AppendWhen( - in.GetPreserve(), + ).Append( "cleanup", StopAllPods, ).Append( "dbus", StopDBus, - ).AppendWhen( - !in.GetPreserve() && (r.Config().Machine().Type() != machine.TypeWorker), - "leave", - LeaveEtcd, ).Append( "stopServices", StopServicesEphemeral, @@ -505,9 +489,6 @@ func (*Sequencer) Upgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []ru "unmountSystem", UnmountEphemeralPartition, UnmountStatePartition, - ).Append( - "verifyDisk", - VerifyDiskAvailability, ).Append( "upgrade", Upgrade, diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index ab4fe1b6c02..e05b200cc39 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -33,23 +33,20 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" pprocfs "github.com/prometheus/procfs" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - "github.com/siderolabs/go-blockdevice/blockdevice/util" + "github.com/siderolabs/go-blockdevice/v2/blkid" + "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" - "github.com/siderolabs/go-retry/retry" clientv3 "go.etcd.io/etcd/client/v3" "golang.org/x/sys/unix" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" - installer "github.com/siderolabs/talos/cmd/installer/pkg/install" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/disk" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/emergency" "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/platform" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" @@ -62,6 +59,7 @@ import ( "github.com/siderolabs/talos/internal/pkg/logind" "github.com/siderolabs/talos/internal/pkg/meta" "github.com/siderolabs/talos/internal/pkg/mount" + mountv2 "github.com/siderolabs/talos/internal/pkg/mount/v2" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/internal/pkg/secureboot" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" @@ -71,11 +69,10 @@ import ( "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/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" metamachinery "github.com/siderolabs/talos/pkg/machinery/meta" + blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" 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" @@ -468,7 +465,7 @@ func SaveConfig(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { 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 { - log.Println("skipping memory size check in the container") + logger.Println("skipping memory size check in the container") return nil } @@ -490,17 +487,17 @@ func MemorySizeCheck(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) switch memTotal := pointer.SafeDeref(info.MemTotal) * humanize.KiByte; { case memTotal < minimum: - log.Println("WARNING: memory size is less than recommended") - log.Println("WARNING: Talos may not work properly") - log.Println("WARNING: minimum memory size is", minimum/humanize.MiByte, "MiB") - log.Println("WARNING: recommended memory size is", recommended/humanize.MiByte, "MiB") - log.Println("WARNING: current total memory size is", memTotal/humanize.MiByte, "MiB") + 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: - log.Println("NOTE: recommended memory size is", recommended/humanize.MiByte, "MiB") - log.Println("NOTE: current total memory size is", memTotal/humanize.MiByte, "MiB") + logger.Println("NOTE: recommended memory size is", recommended/humanize.MiByte, "MiB") + logger.Println("NOTE: current total memory size is", memTotal/humanize.MiByte, "MiB") default: - log.Println("memory size is OK") - log.Println("memory size is", memTotal/humanize.MiByte, "MiB") + logger.Println("memory size is OK") + logger.Println("memory size is", memTotal/humanize.MiByte, "MiB") } return nil @@ -511,29 +508,26 @@ func MemorySizeCheck(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) 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 { - log.Println("skipping disk size check in the container") + logger.Println("skipping disk size check in the container") return nil } - disk := r.State().Machine().Disk() // get ephemeral disk state - if disk == nil { - return errors.New("failed to get ephemeral disk state") - } - - diskSize, err := disk.Size() + ephemeralStatus, err := waitForVolumeReady(ctx, r, constants.EphemeralPartitionLabel) if err != nil { - return fmt.Errorf("failed to get ephemeral disk size: %w", err) + return err } + diskSize := ephemeralStatus.TypedSpec().Size + if minimum := minimal.DiskSize(); diskSize < minimum { - log.Println("WARNING: disk size is less than recommended") - log.Println("WARNING: Talos may not work properly") - log.Println("WARNING: minimum recommended disk size is", minimum/humanize.MiByte, "MiB") - log.Println("WARNING: current total disk size is", diskSize/humanize.MiByte, "MiB") + 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 { - log.Println("disk size is OK") - log.Println("disk size is", diskSize/humanize.MiByte, "MiB") + logger.Println("disk size is OK") + logger.Println("disk size is", diskSize/humanize.MiByte, "MiB") } return nil @@ -848,135 +842,59 @@ func SetupVarDirectory(runtime.Sequence, any) (runtime.TaskExecutionFunc, string // MountUserDisks represents the MountUserDisks task. func MountUserDisks(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - if err = partitionAndFormatDisks(logger, r); err != nil { - return err - } - - return mountDisks(logger, r) - }, "mountUserDisks" -} - -// TODO(andrewrynhard): We shouldn't pull in the installer command package -// here. -func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error { - m := &installer.Manifest{ - Devices: map[string]installer.Device{}, - Targets: map[string][]*installer.Target{}, - Printf: logger.Printf, - } - - for _, disk := range r.Config().Machine().Disks() { - if err := func() error { - bd, err := blockdevice.Open(disk.Device(), blockdevice.WithMode(blockdevice.ReadonlyMode), blockdevice.WithExclusiveLock(true)) - if err != nil { - return err - } - - deviceName := bd.Device().Name() - - if disk.Device() != deviceName { - logger.Printf("using device name %q instead of %q", deviceName, disk.Device()) - } - - //nolint:errcheck - defer bd.Close() - - var pt *gpt.GPT - - pt, err = bd.PartitionTable() - if err != nil { - if !errors.Is(err, blockdevice.ErrMissingPartitionTable) { - return err - } - } - - // Partitions will be created/recreated if either of the following - // conditions are true: - // - a partition table exists AND there are no partitions - // - a partition table does not exist - - if pt != nil { - if len(pt.Partitions().Items()) > 0 { - logger.Printf(("skipping setup of %q, found existing partitions"), deviceName) - - return nil - } - } - - m.Devices[deviceName] = installer.Device{ - Device: deviceName, - ResetPartitionTable: true, - SkipOverlayMountsCheck: true, - } - - for _, part := range disk.Partitions() { - extraTarget := &installer.Target{ - Device: deviceName, - FormatOptions: &partition.FormatOptions{ - Force: true, - FileSystemType: partition.FilesystemTypeXFS, - }, - Options: &partition.Options{ - Size: part.Size(), - PartitionType: partition.LinuxFilesystemData, - }, - } - - m.Targets[deviceName] = append(m.Targets[deviceName], extraTarget) - } - - return nil - }(); err != nil { + 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 + }), + ) + if err != nil { return err } - } - return m.Execute() -} - -func mountDisks(logger *log.Logger, r runtime.Runtime) (err error) { - mountpoints := mount.NewMountPoints() - - for _, disk := range r.Config().Machine().Disks() { - bd, err := blockdevice.Open(disk.Device(), blockdevice.WithMode(blockdevice.ReadonlyMode), blockdevice.WithExclusiveLock(true)) + // fetch user disk volume configs + volumeConfigs, err := safe.StateListAll[*blockres.VolumeConfig](ctx, r.State().V1Alpha2().Resources(), state.WithLabelQuery(resource.LabelExists(blockres.UserDiskLabel))) if err != nil { return err } - deviceName := bd.Device().Name() - - if disk.Device() != deviceName { - logger.Printf("using device name %q instead of %q", deviceName, disk.Device()) + if volumeConfigs.Len() == 0 { + // no user disks + return nil } - if err = bd.Close(); err != nil { - return err - } + var mountpoints mountv2.Points - for i, part := range disk.Partitions() { - var partname string + // wait for volume statuses to be ready + for iter := volumeConfigs.Iterator(); iter.Next(); { + volumeConfig := iter.Value() - partname, err = util.PartPath(deviceName, i+1) + volumeStatus, err := safe.StateWatchFor[*blockres.VolumeStatus](ctx, + r.State().V1Alpha2().Resources(), + blockres.NewVolumeStatus(volumeConfig.Metadata().Namespace(), volumeConfig.Metadata().ID()).Metadata(), + state.WithEventTypes(state.Created, state.Updated), + state.WithCondition(func(r resource.Resource) (bool, error) { + return r.(*blockres.VolumeStatus).TypedSpec().Phase == blockres.VolumePhaseReady, nil + }), + ) if err != nil { - return err + return fmt.Errorf("failed to watch for volume status %s: %w", volumeConfig.Metadata().ID(), err) } - if _, err = os.Stat(part.MountPoint()); errors.Is(err, os.ErrNotExist) { - if err = os.MkdirAll(part.MountPoint(), 0o700); err != nil { - return err - } - } - - mountpoints.Set(partname, - mount.NewMountPoint(partname, part.MountPoint(), "xfs", unix.MS_NOATIME, "", - mount.WithProjectQuota(r.Config().Machine().Features().DiskQuotaSupportEnabled()), - ), - ) + mountpoints = append(mountpoints, mountv2.NewPoint( + volumeStatus.TypedSpec().MountLocation, + volumeConfig.TypedSpec().Mount.TargetPath, + volumeStatus.TypedSpec().Filesystem.String(), + )) } - } - return mount.Mount(mountpoints) + _, err = mountpoints.Mount(mountv2.WithMountPrinter(logger.Printf)) + + return err + }, "mountUserDisks" } // WriteUserFiles represents the WriteUserFiles task. @@ -1185,42 +1103,44 @@ func UnmountOverlayFilesystems(runtime.Sequence, any) (runtime.TaskExecutionFunc // UnmountUserDisks represents the UnmountUserDisks task. func UnmountUserDisks(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - if r.Config() == nil { - return nil + return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { + // fetch user disk volume configs + volumeConfigs, err := safe.StateListAll[*blockres.VolumeConfig](ctx, r.State().V1Alpha2().Resources(), state.WithLabelQuery(resource.LabelExists(blockres.UserDiskLabel))) + if err != nil { + return err } - mountpoints := mount.NewMountPoints() + if volumeConfigs.Len() == 0 { + // no user disks + return nil + } - for _, disk := range r.Config().Machine().Disks() { - bd, err := blockdevice.Open(disk.Device(), blockdevice.WithMode(blockdevice.ReadonlyMode)) - if err != nil { - return err - } + var mountpoints mountv2.Points - deviceName := bd.Device().Name() + for iter := volumeConfigs.Iterator(); iter.Next(); { + volumeConfig := iter.Value() - if deviceName != disk.Device() { - logger.Printf("using device name %q instead of %q", deviceName, disk.Device()) + volumeStatus, err := safe.StateGetByID[*blockres.VolumeStatus]( + ctx, + r.State().V1Alpha2().Resources(), + volumeConfig.Metadata().ID(), + ) + if err != nil { + continue } - if err = bd.Close(); err != nil { - return err + if volumeStatus.TypedSpec().Phase != blockres.VolumePhaseReady { + continue } - for i, part := range disk.Partitions() { - var partname string - - partname, err = util.PartPath(deviceName, i+1) - if err != nil { - return err - } - - mountpoints.Set(partname, mount.NewMountPoint(partname, part.MountPoint(), "xfs", unix.MS_NOATIME, "")) - } + mountpoints = append(mountpoints, mountv2.NewPoint( + volumeStatus.TypedSpec().MountLocation, + volumeConfig.TypedSpec().Mount.TargetPath, + volumeStatus.TypedSpec().Filesystem.String(), + )) } - return mount.Unmount(mountpoints) + return mountpoints.Unmount() }, "unmountUserDisks" } @@ -1264,12 +1184,16 @@ func UnmountPodMounts(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) // UnmountSystemDiskBindMounts represents the UnmountSystemDiskBindMounts task. func UnmountSystemDiskBindMounts(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - systemDisk := r.State().Machine().Disk() + systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) + if err != nil { + return err + } + if systemDisk == nil { return nil } - devname := systemDisk.BlockDevice.Device().Name() + devname := systemDisk.DevPath f, err := os.Open("/proc/mounts") if err != nil { @@ -1546,7 +1470,7 @@ func ResetSystemDiskPartitions(seq runtime.Sequence, _ any) (runtime.TaskExecuti } return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { - targets, err := parseTargets(r, *wipeStr) + targets, err := parseTargets(ctx, r, *wipeStr) if err != nil { return err } @@ -1579,20 +1503,25 @@ func ResetSystemDiskPartitions(seq runtime.Sequence, _ any) (runtime.TaskExecuti // ResetSystemDisk represents the task to reset the system disk. func ResetSystemDisk(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - var dev *blockdevice.BlockDevice - - disk := r.State().Machine().Disk() + 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 disk == nil { + if systemDisk == nil { return nil } - dev, err = blockdevice.Open(disk.Device().Name()) + dev, err := block.NewFromPath(systemDisk.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", systemDisk.DevPath, err) + } + defer dev.Close() //nolint:errcheck return dev.FastWipe() @@ -1608,7 +1537,7 @@ func ResetUserDisks(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, st } wipeDevice := func(deviceName string) error { - dev, err := blockdevice.Open(deviceName) + dev, err := block.NewFromPath(deviceName, block.OpenForWrite()) if err != nil { return err } @@ -1619,6 +1548,12 @@ func ResetUserDisks(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, st } }() + 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) return dev.FastWipe() @@ -1635,37 +1570,33 @@ func ResetUserDisks(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, st } type targets struct { - systemDiskTargets []*installer.Target + systemDiskTargets []*partition.VolumeWipeTarget } func (opt targets) GetSystemDiskTargets() []runtime.PartitionTarget { - return xslices.Map(opt.systemDiskTargets, func(t *installer.Target) runtime.PartitionTarget { return t }) + return xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) runtime.PartitionTarget { return t }) } -func parseTargets(r runtime.Runtime, wipeStr string) (targets, error) { +func parseTargets(ctx context.Context, r runtime.Runtime, wipeStr string) (targets, error) { after, found := strings.CutPrefix(wipeStr, "system:") if !found { return targets{}, fmt.Errorf("invalid wipe labels string: %q", wipeStr) } - var result []*installer.Target //nolint:prealloc + var result []*partition.VolumeWipeTarget //nolint:prealloc - for _, part := range strings.Split(after, ",") { - bd := r.State().Machine().Disk().BlockDevice - - target, err := installer.ParseTarget(part, bd.Device().Name()) + for _, label := range strings.Split(after, ",") { + volumeStatus, err := safe.ReaderGetByID[*blockres.VolumeStatus](ctx, r.State().V1Alpha2().Resources(), label) if err != nil { - return targets{}, fmt.Errorf("error parsing target label %q: %w", part, err) + return targets{}, fmt.Errorf("failed to get volume status with label %q: %w", label, err) } - pt, err := bd.PartitionTable() - if err != nil { - return targets{}, fmt.Errorf("error reading partition table: %w", err) + if volumeStatus.TypedSpec().Phase != blockres.VolumePhaseReady { + return targets{}, fmt.Errorf("failed to reset: volume %q is not ready", label) } - _, err = target.Locate(pt) - if err != nil { - return targets{}, fmt.Errorf("error locating partition %q: %w", part, err) + target := &partition.VolumeWipeTarget{ + VolumeStatus: volumeStatus, } result = append(result, target) @@ -1693,7 +1624,7 @@ func ResetSystemDiskSpec(_ runtime.Sequence, data any) (runtime.TaskExecutionFun } for _, target := range in.GetSystemDiskTargets() { - if err = target.Format(logger.Printf); err != nil { + if err = target.Wipe(ctx, logger.Printf); err != nil { return fmt.Errorf("failed wiping partition %s: %w", target, err) } } @@ -1729,77 +1660,6 @@ func ResetSystemDiskSpec(_ runtime.Sequence, data any) (runtime.TaskExecutionFun }, "resetSystemDiskSpec" } -// VerifyDiskAvailability represents the task for verifying that the system -// disk is not in use. -func VerifyDiskAvailability(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - devname := r.State().Machine().Disk().BlockDevice.Device().Name() - - // We MUST close this in order to avoid EBUSY. - if err = r.State().Machine().Close(); err != nil { - return err - } - - // TODO(andrewrynhard): This should be more dynamic. If we ever change the - // partition scheme there is the chance that 2 is not the correct parition to - // check. - partname, err := util.PartPath(devname, 2) - if err != nil { - return err - } - - if _, err = os.Stat(partname); errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("ephemeral partition not found: %w", err) - } - - mountsReported := false - - return retry.Constant(3*time.Minute, retry.WithUnits(500*time.Millisecond)).Retry(func() error { - if err = tryLock(partname); err != nil { - if err == unix.EBUSY { - if !mountsReported { - // if disk is busy, report mounts for debugging purposes but just once - // otherwise console might be flooded with messages - dumpMounts(logger) - - mountsReported = true - } - - return retry.ExpectedErrorf("ephemeral partition in use: %q", partname) - } - - return fmt.Errorf("failed to verify ephemeral partition not in use: %w", err) - } - - return nil - }) - }, "verifyDiskAvailability" -} - -func tryLock(path string) error { - fd, errno := unix.Open(path, unix.O_RDONLY|unix.O_EXCL|unix.O_CLOEXEC, 0) - - //nolint:errcheck - defer unix.Close(fd) - - return errno -} - -func dumpMounts(logger *log.Logger) { - mounts, err := os.Open("/proc/mounts") - if err != nil { - logger.Printf("failed to read mounts: %s", err) - - return - } - - defer mounts.Close() //nolint:errcheck - - logger.Printf("contents of /proc/mounts:") - - _, _ = io.Copy(log.Writer(), mounts) //nolint:errcheck -} - // 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) { @@ -1810,7 +1670,16 @@ func Upgrade(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return runtime.ErrInvalidSequenceData } - devname := r.State().Machine().Disk().BlockDevice.Device().Name() + 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()) @@ -1910,58 +1779,30 @@ func SaveStateEncryptionConfig(runtime.Sequence, any) (runtime.TaskExecutionFunc }, "SaveStateEncryptionConfig" } -// MountEFIPartition mounts the EFI partition. -func MountEFIPartition(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - return mount.SystemPartitionMount(ctx, r, logger, constants.EFIPartitionLabel) - }, "mountEFIPartition" -} - -// UnmountEFIPartition unmounts the EFI partition. -func UnmountEFIPartition(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { - return mount.SystemPartitionUnmount(r, logger, constants.EFIPartitionLabel) - }, "unmountEFIPartition" -} - // MountStatePartition mounts the system partition. -func MountStatePartition(seq runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { - return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { - flags := mount.SkipIfMounted - - if seq == runtime.SequenceInitialize { - flags |= mount.SkipIfNoFilesystem - } - - opts := []mount.Option{mount.WithFlags(flags)} - - var encryption config.Encryption - // first try reading encryption from the config - // which always has the priority here - if r.Config() != nil && r.Config().Machine() != nil { - encryption = r.Config().Machine().SystemDiskEncryption().Get(constants.StatePartitionLabel) - } - - // then try reading it from the META partition - if encryption == nil { - var encryptionFromMeta *v1alpha1.EncryptionConfig - - data, ok := r.State().Machine().Meta().ReadTagBytes(meta.StateEncryptionConfig) - if ok { - if err = json.Unmarshal(data, &encryptionFromMeta); err != nil { +func MountStatePartition(required bool) func(seq runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { + return func(seq runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { + return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { + if required { + if _, err := waitForVolumeReady(ctx, r, constants.StatePartitionLabel); err != nil { + return err + } + } else { + volumeStatus, err := waitForVolumeReadyOrMissing(ctx, r, constants.StatePartitionLabel) + if err != nil { return err } - encryption = encryptionFromMeta - } - } + if volumeStatus.TypedSpec().Phase == blockres.VolumePhaseMissing { + logger.Print("STATE volume is missing") - if encryption != nil { - opts = append(opts, mount.WithEncryptionConfig(encryption), mount.WithSystemInformationGetter(r.GetSystemInformation)) - } + return nil + } + } - return mount.SystemPartitionMount(ctx, r, logger, constants.StatePartitionLabel, opts...) - }, "mountStatePartition" + return mount.SystemPartitionMount(ctx, r, logger, constants.StatePartitionLabel) + }, "mountStatePartition" + } } // UnmountStatePartition unmounts the system partition. @@ -1974,9 +1815,12 @@ func UnmountStatePartition(runtime.Sequence, any) (runtime.TaskExecutionFunc, st // 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 { + if _, err := waitForVolumeReady(ctx, r, constants.EphemeralPartitionLabel); err != nil { + return err + } + return mount.SystemPartitionMount(ctx, r, logger, constants.EphemeralPartitionLabel, - mount.WithFlags(mount.Resize), - mount.WithProjectQuota(r.Config().Machine().Features().DiskQuotaSupportEnabled())) + mountv2.WithProjectQuota(r.Config().Machine().Features().DiskQuotaSupportEnabled())) }, "mountEphemeralPartition" } @@ -1988,6 +1832,8 @@ func UnmountEphemeralPartition(runtime.Sequence, any) (runtime.TaskExecutionFunc } // Install mounts or installs the system partitions. +// +//nolint:gocyclo func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { switch { @@ -2004,6 +1850,11 @@ func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return err } + disk, err = filepath.EvalSymlinks(disk) + if err != nil { + return err + } + err = install.RunInstallerContainer( disk, r.State().Platform().Name(), @@ -2040,7 +1891,16 @@ func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { logger.Println("install successful") case r.State().Machine().IsInstallStaged(): - devname := r.State().Machine().Disk().BlockDevice.Device().Name() + 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 @@ -2117,97 +1977,105 @@ func KexecPrepare(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, stri } } - if r.Config() == nil { - return nil + systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) + if err != nil { + return err } - // check if partition with label BOOT exists - if device := r.State().Machine().Disk(disk.WithPartitionLabel(constants.BootPartitionLabel)); device == nil { - return nil + if systemDisk == nil { + return nil // no system disk, no kexec } - // BOOT partition exists and we can mount it - if err := mount.SystemPartitionMount(ctx, r, logger, constants.BootPartitionLabel); err != nil { + dev, err := block.NewFromPath(systemDisk.DevPath) + if err != nil { return err } - defer mount.SystemPartitionUnmount(r, logger, constants.BootPartitionLabel) //nolint:errcheck + defer dev.Close() //nolint:errcheck - conf, err := grub.Read(grub.ConfigPath) - if err != nil { - return err - } + if err = dev.RetryLockWithTimeout(ctx, false, 3*time.Minute); err != nil { + log.Print("kexec skipped as system disk is busy") - if conf == nil { return nil } - defaultEntry, ok := conf.Entries[conf.Default] - if !ok { - return nil - } + defer dev.Unlock() //nolint:errcheck - kernelPath := filepath.Join(constants.BootMountPoint, defaultEntry.Linux) - initrdPath := filepath.Join(constants.BootMountPoint, defaultEntry.Initrd) + _, err = grub.ProbeWithCallback(systemDisk.DevPath, + options.ProbeOptions{ + BlockProbeOptions: []blkid.ProbeOption{blkid.WithSkipLocking(true)}, + }, + func(conf *grub.Config) error { + defaultEntry, ok := conf.Entries[conf.Default] + if !ok { + return nil + } - kernel, err := os.Open(kernelPath) - if err != nil { - return err - } + kernelPath := filepath.Join(constants.BootMountPoint, defaultEntry.Linux) + initrdPath := filepath.Join(constants.BootMountPoint, defaultEntry.Initrd) - defer kernel.Close() //nolint:errcheck + kernel, err := os.Open(kernelPath) + if err != nil { + return err + } - fd := int(kernel.Fd()) + defer kernel.Close() //nolint:errcheck - // on arm64 we need to extract the kernel from the zboot image if it's compressed - if goruntime.GOARCH == "arm64" { - var fileCloser io.Closer + fd := int(kernel.Fd()) - fd, fileCloser, err = zboot.Extract(kernel) - if err != nil { - return err - } + // on arm64 we need to extract the kernel from the zboot image if it's compressed + if goruntime.GOARCH == "arm64" { + var fileCloser io.Closer - defer func() { - if fileCloser != nil { - fileCloser.Close() //nolint:errcheck + fd, fileCloser, err = zboot.Extract(kernel) + if err != nil { + return err + } + + defer func() { + if fileCloser != nil { + fileCloser.Close() //nolint:errcheck + } + }() } - }() - } - initrd, err := os.Open(initrdPath) - if err != nil { - return err - } + initrd, err := os.Open(initrdPath) + if err != nil { + return err + } - defer initrd.Close() //nolint:errcheck + defer initrd.Close() //nolint:errcheck - cmdline := strings.TrimSpace(defaultEntry.Cmdline) + cmdline := strings.TrimSpace(defaultEntry.Cmdline) - if err = unix.KexecFileLoad(fd, int(initrd.Fd()), cmdline, 0); err != nil { - switch { - case errors.Is(err, unix.ENOSYS): - log.Printf("kexec support is disabled in the kernel") + if err = unix.KexecFileLoad(fd, int(initrd.Fd()), 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.EPERM): + log.Printf("kexec support is disabled via sysctl") - return nil - case errors.Is(err, unix.EBUSY): - log.Printf("kexec is busy") + 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) - } - } + return nil + default: + return fmt.Errorf("error loading kernel for kexec: %w", err) + } + } - log.Printf("prepared kexec environment kernel=%q initrd=%q cmdline=%q", kernelPath, initrdPath, cmdline) + log.Printf("prepared kexec environment kernel=%q initrd=%q cmdline=%q", kernelPath, initrdPath, cmdline) - r.State().Machine().KexecPrepared(true) + r.State().Machine().KexecPrepared(true) - return nil + return nil + }, + ) + + return err }, "kexecPrepare" } @@ -2305,6 +2173,11 @@ func ReloadMeta(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { // 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" } @@ -2430,3 +2303,11 @@ func logError(err error, logger *log.Logger) error { 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) +} + +func waitForVolumeReadyOrMissing(ctx context.Context, r runtime.Runtime, volumeID string) (*blockres.VolumeStatus, error) { + return blockres.WaitForVolumePhase(ctx, r.State().V1Alpha2().Resources(), volumeID, blockres.VolumePhaseReady, blockres.VolumePhaseMissing) +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go index 0eb0cb1543a..6f58f7a45d0 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go @@ -6,21 +6,21 @@ package v1alpha1 import ( "context" - "errors" "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" - multierror "github.com/hashicorp/go-multierror" - "github.com/siderolabs/go-blockdevice/blockdevice/probe" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/disk" "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" + "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // State implements the state interface. @@ -36,8 +36,6 @@ type MachineState struct { platform runtime.Platform resources state.State - disks map[string]*probe.ProbedBlockDevice - meta *meta.Meta metaOnce sync.Once @@ -70,13 +68,6 @@ func NewState() (s *State, err error) { resources: v2State.Resources(), } - err = machine.probeDisks() - if err != nil { - if !errors.Is(err, os.ErrNotExist) { - return nil, err - } - } - cluster := &ClusterState{} s = &State{ @@ -109,37 +100,6 @@ func (s *State) V1Alpha2() runtime.V1Alpha2State { return s.v2 } -func (s *MachineState) probeDisks(labels ...string) error { - if s.platform.Mode() == runtime.ModeContainer { - return os.ErrNotExist - } - - if len(labels) == 0 { - labels = []string{constants.EphemeralPartitionLabel, constants.BootPartitionLabel, constants.EFIPartitionLabel, constants.StatePartitionLabel} - } - - if s.disks == nil { - s.disks = map[string]*probe.ProbedBlockDevice{} - } - - for _, label := range labels { - if _, ok := s.disks[label]; ok { - continue - } - - var dev *probe.ProbedBlockDevice - - dev, err := probe.GetDevWithPartitionName(label) - if err != nil { - return err - } - - s.disks[label] = dev - } - - return nil -} - // Meta implements the runtime.MachineState interface. func (s *MachineState) Meta() runtime.Meta { // no META in container mode @@ -261,44 +221,45 @@ func (s *MachineState) probeMeta() { } } -// Disk implements the machine state interface. -func (s *MachineState) Disk(options ...disk.Option) *probe.ProbedBlockDevice { - opts := &disk.Options{ - Label: constants.EphemeralPartitionLabel, - } - - for _, opt := range options { - opt(opts) +// Installed implements the machine state interface. +func (s *MachineState) Installed() bool { + // undefined in container mode + if s.platform.Mode() == runtime.ModeContainer { + return true } - s.probeDisks(opts.Label) //nolint:errcheck - - return s.disks[opts.Label] -} - -// Close implements the machine state interface. -func (s *MachineState) Close() error { - var result *multierror.Error - - for label, disk := range s.disks { - if err := disk.Close(); err != nil { - e := multierror.Append(result, err) - if e != nil { - return e + // 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 } - } - delete(s.disks, label) + 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 result.ErrorOrNil() -} - -// Installed implements the machine state interface. -func (s *MachineState) Installed() bool { - return s.Disk( - disk.WithPartitionLabel(constants.EphemeralPartitionLabel), - ) != nil + return metaStatus.TypedSpec().Phase == block.VolumePhaseReady } // IsInstallStaged implements the machine state interface. diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index 2eefb73f806..0a9eb8c0602 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -94,6 +94,9 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error &block.DiscoveryController{}, &block.DisksController{}, &block.SystemDiskController{}, + &block.UserDiskConfigController{}, + &block.VolumeConfigController{}, + &block.VolumeManagerController{}, &cluster.AffiliateMergeController{}, cluster.NewConfigController(), &cluster.DiscoveryServiceController{}, diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go index 4354d73c853..019b5d5d0c0 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go @@ -97,8 +97,13 @@ func NewState() (*State, error) { for _, r := range []meta.ResourceWithRD{ &block.Device{}, &block.DiscoveredVolume{}, + &block.DiscoveryRefreshRequest{}, + &block.DiscoveryRefreshStatus{}, &block.Disk{}, &block.SystemDisk{}, + &block.UserDiskConfigStatus{}, + &block.VolumeConfig{}, + &block.VolumeStatus{}, &cluster.Affiliate{}, &cluster.Config{}, &cluster.Identity{}, diff --git a/internal/app/machined/revert.go b/internal/app/machined/revert.go index bd48c3ca078..c106eee2780 100644 --- a/internal/app/machined/revert.go +++ b/internal/app/machined/revert.go @@ -9,7 +9,6 @@ import ( "log" "os" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/pkg/meta" ) @@ -45,6 +44,7 @@ func revertBootloadInternal(ctx context.Context) error { log.Printf("reverting failed upgrade, switching to %q", label) + /* [TODO] restore me if err = func() error { config, probeErr := bootloader.Probe(ctx, "") if probeErr != nil { @@ -60,6 +60,7 @@ func revertBootloadInternal(ctx context.Context) error { }(); err != nil { return err } + */ if _, err = metaState.DeleteTag(ctx, meta.Upgrade); err != nil { return err diff --git a/internal/app/maintenance/server.go b/internal/app/maintenance/server.go index 6c7f45df88f..ad95d42d863 100644 --- a/internal/app/maintenance/server.go +++ b/internal/app/maintenance/server.go @@ -16,14 +16,13 @@ import ( "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/server" "github.com/google/uuid" - "github.com/siderolabs/go-blockdevice/blockdevice" + "github.com/siderolabs/go-blockdevice/v2/block" "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/machined/pkg/runtime" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/disk" "github.com/siderolabs/talos/internal/app/resources" storaged "github.com/siderolabs/talos/internal/app/storaged" "github.com/siderolabs/talos/internal/pkg/configuration" @@ -33,7 +32,7 @@ import ( "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" v1alpha1machine "github.com/siderolabs/talos/pkg/machinery/config/machine" - "github.com/siderolabs/talos/pkg/machinery/constants" + blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/machinery/version" ) @@ -167,7 +166,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (reply return nil, err } - if s.controller.Runtime().State().Machine().Disk() == nil { + if !s.controller.Runtime().State().Machine().Installed() { return nil, status.Errorf(codes.FailedPrecondition, "Talos is not installed") } @@ -211,8 +210,8 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (reply // Reset resets the node. // //nolint:gocyclo -func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *machine.ResetResponse, err error) { - if err = s.assertAdminRole(ctx); err != nil { +func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (*machine.ResetResponse, error) { + if err := s.assertAdminRole(ctx); err != nil { return nil, err } @@ -228,22 +227,23 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma return nil, errors.New("system partitions to wipe params is not supported in the maintenance mode") } - var dev *blockdevice.BlockDevice - - disk := s.controller.Runtime().State().Machine().Disk(disk.WithPartitionLabel(constants.BootPartitionLabel)) + systemDisk, err := blockres.GetSystemDisk(ctx, s.controller.Runtime().State().V1Alpha2().Resources()) + if err != nil { + return nil, err + } - if disk == nil { + if systemDisk == nil { return nil, errors.New("reset failed: Talos is not installed") } - dev, err = blockdevice.Open(disk.Device().Name()) + dev, err := block.NewFromPath(systemDisk.DevPath, block.OpenForWrite()) if err != nil { return nil, err } defer dev.Close() //nolint:errcheck - if err = dev.Reset(); err != nil { + if err = dev.FastWipe(); err != nil { return nil, err } @@ -251,9 +251,7 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma if in.Mode != machine.ResetRequest_SYSTEM_DISK { for _, deviceName := range in.UserDisksToWipe { - var dev *blockdevice.BlockDevice - - dev, err = blockdevice.Open(deviceName) + dev, err = block.NewFromPath(deviceName, block.OpenForWrite()) if err != nil { return nil, err } @@ -282,15 +280,13 @@ func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *ma } }() - reply = &machine.ResetResponse{ + return &machine.ResetResponse{ Messages: []*machine.Reset{ { ActorId: actorID, }, }, - } - - return reply, nil + }, nil } // MetaWrite implements the [machine.MachineServiceServer] interface. diff --git a/internal/integration/provision/provision.go b/internal/integration/provision/provision.go index 65a87e61ec4..d42140e15e4 100644 --- a/internal/integration/provision/provision.go +++ b/internal/integration/provision/provision.go @@ -19,7 +19,7 @@ import ( "time" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" + "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "github.com/siderolabs/go-retry/retry" sideronet "github.com/siderolabs/net" diff --git a/internal/pkg/encryption/encryption.go b/internal/pkg/encryption/encryption.go index 0fb2391598a..079ce31a8b1 100644 --- a/internal/pkg/encryption/encryption.go +++ b/internal/pkg/encryption/encryption.go @@ -16,119 +16,86 @@ import ( "time" "github.com/hashicorp/go-multierror" - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/luks" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - "github.com/siderolabs/go-retry/retry" + "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/config/config" + "github.com/siderolabs/talos/pkg/machinery/resources/block" ) -const ( - keyFetchTimeout = time.Minute * 5 - keyHandlerTimeout = time.Second * 10 -) +const keyHandlerTimeout = time.Second * 10 // NewHandler creates new Handler. -func NewHandler(device *blockdevice.BlockDevice, partition *gpt.Partition, encryptionConfig config.Encryption, getSystemInformation helpers.SystemInformationGetter) (*Handler, error) { - var provider encryption.Provider +func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, getSystemInformation helpers.SystemInformationGetter) (*Handler, error) { + cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher) + if err != nil { + return nil, fmt.Errorf("failed to parse cipher kind: %w", err) + } - switch encryptionConfig.Provider() { - case encryption.LUKS2: - cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher()) - if err != nil { - return nil, err - } + var opts []luks.Option - var opts []luks.Option - if encryptionConfig.KeySize() != 0 { - opts = append(opts, luks.WithKeySize(encryptionConfig.KeySize())) - } + if encryptionConfig.KeySize != 0 { + opts = append(opts, luks.WithKeySize(encryptionConfig.KeySize)) + } - if encryptionConfig.BlockSize() != 0 { - opts = append(opts, luks.WithBlockSize(encryptionConfig.BlockSize())) - } + if encryptionConfig.BlockSize != 0 { + opts = append(opts, luks.WithBlockSize(encryptionConfig.BlockSize)) + } - if encryptionConfig.Options() != nil { - for _, opt := range encryptionConfig.Options() { - if err = luks.ValidatePerfOption(opt); err != nil { - return nil, err - } + 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)) - opts = append(opts, luks.WithPerfOptions(encryptionConfig.Options()...)) + for _, cfg := range encryptionConfig.Keys { + handler, err := keys.NewHandler(cfg, keys.WithVolumeID(volumeID), keys.WithSystemInformationGetter(getSystemInformation)) + if err != nil { + return nil, err } - provider = luks.New( - cipher, - opts..., - ) - default: - return nil, fmt.Errorf("unknown encryption kind %s", encryptionConfig.Provider()) + keyHandlers = append(keyHandlers, handler) } + //nolint:scopelint + sort.Slice(keyHandlers, func(i, j int) bool { return keyHandlers[i].Slot() < keyHandlers[j].Slot() }) + + provider := luks.New( + cipher, + opts..., + ) + return &Handler{ - device: device, - partition: partition, - encryptionConfig: encryptionConfig, - encryptionProvider: provider, - getSystemInformation: getSystemInformation, + encryptionProvider: provider, + keyHandlers: keyHandlers, }, nil } // Handler reads encryption config, creates appropriate // encryption provider, handles encrypted partition open and close. type Handler struct { - device *blockdevice.BlockDevice - partition *gpt.Partition - encryptionConfig config.Encryption - encryptionProvider encryption.Provider - getSystemInformation helpers.SystemInformationGetter - encryptedPath string + encryptionProvider encryption.Provider + keyHandlers []keys.Handler } // Open encrypted partition. -// -//nolint:gocyclo -func (h *Handler) Open(ctx context.Context) (string, error) { - partPath, err := h.partition.Path() - if err != nil { - return "", err - } - - sb, err := h.partition.SuperBlock() - if err != nil { - return "", err - } - - handlers, err := h.initKeyHandlers(h.encryptionConfig, h.partition) - if err != nil { - return "", err - } - - // encrypt if partition is not encrypted and empty - if sb == nil { - err = h.formatAndEncrypt(ctx, partPath, handlers) - if err != nil { - return "", err - } - } else if sb.Type() != h.encryptionConfig.Provider() { - return "", fmt.Errorf("failed to encrypt the partition %s, because it is not empty", partPath) - } - +func (h *Handler) Open(ctx context.Context, logger *zap.Logger, devicePath, encryptedName string) (string, error) { var ( path string key *encryption.Key ) - if err = h.tryHandlers(ctx, handlers, func(ctx context.Context, handler keys.Handler) error { - var token token.Token - - token, err = h.readToken(partPath, handler.Slot()) + if err := h.tryHandlers(ctx, logger, func(ctx context.Context, handler keys.Handler) error { + token, err := h.readToken(devicePath, handler.Slot()) if err != nil { return err } @@ -137,46 +104,35 @@ func (h *Handler) Open(ctx context.Context) (string, error) { return err } - path, err = h.encryptionProvider.Open(partPath, key) + path, err = h.encryptionProvider.Open(devicePath, encryptedName, key) if err != nil { return err } return nil }); err != nil { - return "", fmt.Errorf("failed to open encrypted device %s: %w", partPath, err) + return "", fmt.Errorf("failed to open encrypted device %s: %w", devicePath, err) } - log.Printf("mapped encrypted partition %s -> %s", partPath, path) - - if err = h.syncKeys(ctx, partPath, handlers, key); err != nil { + if err := h.syncKeys(ctx, devicePath, key); err != nil { return "", err } - h.encryptedPath = path - return path, nil } // Close encrypted partition. -func (h *Handler) Close() error { - if h.encryptedPath == "" { - return nil - } - - if err := h.encryptionProvider.Close(h.encryptedPath); err != nil { - return err +func (h *Handler) Close(encryptedPath string) error { + if err := h.encryptionProvider.Close(encryptedPath); err != nil { + return fmt.Errorf("error closing %s: %w", encryptedPath, err) } - log.Printf("closed encrypted partition %s", h.encryptedPath) - return nil } -func (h *Handler) formatAndEncrypt(ctx context.Context, path string, handlers []keys.Handler) error { - log.Printf("encrypting the partition %s (%s)", path, h.partition.Name) - - if len(handlers) == 0 { +// FormatAndEncrypt formats and encrypts the volume. +func (h *Handler) FormatAndEncrypt(ctx context.Context, logger *zap.Logger, path string) error { + if len(h.keyHandlers) == 0 { return errors.New("no encryption keys found") } @@ -186,7 +142,7 @@ func (h *Handler) formatAndEncrypt(ctx context.Context, path string, handlers [] err error ) - if err = h.tryHandlers(ctx, handlers, func(ctx context.Context, h keys.Handler) error { + if err = h.tryHandlers(ctx, logger, func(ctx context.Context, h keys.Handler) error { if key, token, err = h.NewKey(ctx); err != nil { return err } @@ -206,7 +162,7 @@ func (h *Handler) formatAndEncrypt(ctx context.Context, path string, handlers [] } } - for _, handler := range handlers { + for _, handler := range h.keyHandlers { if handler.Slot() == key.Slot { continue } @@ -220,7 +176,7 @@ func (h *Handler) formatAndEncrypt(ctx context.Context, path string, handlers [] } //nolint:gocyclo -func (h *Handler) syncKeys(ctx context.Context, path string, handlers []keys.Handler, k *encryption.Key) error { +func (h *Handler) syncKeys(ctx context.Context, path string, k *encryption.Key) error { keyslots, err := h.encryptionProvider.ReadKeyslots(path) if err != nil { return err @@ -228,7 +184,7 @@ func (h *Handler) syncKeys(ctx context.Context, path string, handlers []keys.Han visited := map[string]bool{} - for _, handler := range handlers { + 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 @@ -334,25 +290,7 @@ func (h *Handler) addKey(ctx context.Context, path string, existingKey *encrypti return nil } -func (h *Handler) initKeyHandlers(encryptionConfig config.Encryption, partition *gpt.Partition) ([]keys.Handler, error) { - handlers := make([]keys.Handler, 0, len(encryptionConfig.Keys())) - - for _, cfg := range encryptionConfig.Keys() { - handler, err := keys.NewHandler(cfg, keys.WithPartitionLabel(partition.Name), keys.WithSystemInformationGetter(h.getSystemInformation)) - if err != nil { - return nil, err - } - - handlers = append(handlers, handler) - } - - //nolint:scopelint - sort.Slice(handlers, func(i, j int) bool { return handlers[i].Slot() < handlers[j].Slot() }) - - return handlers, nil -} - -func (h *Handler) tryHandlers(ctx context.Context, handlers []keys.Handler, cb func(ctx context.Context, h keys.Handler) error) error { +func (h *Handler) tryHandlers(ctx context.Context, logger *zap.Logger, cb func(ctx context.Context, h keys.Handler) error) error { callback := func(ctx context.Context, h keys.Handler) error { ctx, cancel := context.WithTimeout(ctx, keyHandlerTimeout) defer cancel() @@ -360,24 +298,21 @@ func (h *Handler) tryHandlers(ctx context.Context, handlers []keys.Handler, cb f return cb(ctx, h) } - return retry.Exponential(keyFetchTimeout, retry.WithUnits(time.Second), retry.WithJitter(time.Second)).RetryWithContext(ctx, - func(ctx context.Context) error { - var errs error + var errs error - for _, h := range handlers { - if err := callback(ctx, h); err != nil { - errs = multierror.Append(errs, err) + for _, h := range h.keyHandlers { + if err := callback(ctx, h); err != nil { + errs = multierror.Append(errs, err) - log.Printf("failed to call key handler at slot %d: %s", h.Slot(), err) + logger.Warn("failed to call key handler", zap.Int("slot", h.Slot()), zap.Error(err)) - continue - } + continue + } - return nil - } + return nil + } - return retry.ExpectedErrorf("no handlers available to get encryption keys from: %w", errs) - }) + return fmt.Errorf("no handlers available to get encryption keys from: %w", errs) } func (h *Handler) readToken(path string, id int) (token.Token, error) { diff --git a/internal/pkg/encryption/keys/keys.go b/internal/pkg/encryption/keys/keys.go index d53ca2cf19d..4232988cbb1 100644 --- a/internal/pkg/encryption/keys/keys.go +++ b/internal/pkg/encryption/keys/keys.go @@ -10,48 +10,48 @@ import ( "errors" "fmt" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" + "github.com/siderolabs/go-blockdevice/v2/encryption" + "github.com/siderolabs/go-blockdevice/v2/encryption/token" - "github.com/siderolabs/talos/pkg/machinery/config/config" + "github.com/siderolabs/talos/pkg/machinery/resources/block" ) var errNoSystemInfoGetter = errors.New("the UUID getter is not set") // NewHandler key using provided config. -func NewHandler(cfg config.EncryptionKey, options ...KeyOption) (Handler, error) { +func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error) { opts, err := NewDefaultOptions(options) if err != nil { return nil, err } - key := KeyHandler{slot: cfg.Slot()} + key := KeyHandler{slot: cfg.Slot} - switch { - case cfg.Static() != nil: - k := cfg.Static().Key() + switch cfg.Type { + case block.EncryptionKeyStatic: + k := cfg.StaticPassphrase if k == nil { return nil, errors.New("static key must have key data defined") } return NewStaticKeyHandler(key, k), nil - case cfg.NodeID() != nil: + case block.EncryptionKeyNodeID: if opts.GetSystemInformation == nil { - return nil, fmt.Errorf("failed to create nodeUUID key handler at slot %d: %w", cfg.Slot(), errNoSystemInfoGetter) + return nil, fmt.Errorf("failed to create nodeUUID key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) } - return NewNodeIDKeyHandler(key, opts.PartitionLabel, opts.GetSystemInformation), nil - case cfg.KMS() != nil: + return NewNodeIDKeyHandler(key, opts.VolumeID, opts.GetSystemInformation), nil + case block.EncryptionKeyKMS: if opts.GetSystemInformation == nil { - return nil, fmt.Errorf("failed to create KMS key handler at slot %d: %w", cfg.Slot(), errNoSystemInfoGetter) + return nil, fmt.Errorf("failed to create KMS key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) } - return NewKMSKeyHandler(key, cfg.KMS().Endpoint(), opts.GetSystemInformation) - case cfg.TPM() != nil: - return NewTPMKeyHandler(key, cfg.TPM().CheckSecurebootOnEnroll()) + return NewKMSKeyHandler(key, cfg.KMSEndpoint, opts.GetSystemInformation) + case block.EncryptionKeyTPM: + return NewTPMKeyHandler(key, cfg.TPMCheckSecurebootStatusOnEnroll) + default: + return nil, fmt.Errorf("unsupported key type: %s", cfg.Type) } - - return nil, errors.New("malformed config: no key handler can be created") } // Handler manages key lifecycle. diff --git a/internal/pkg/encryption/keys/kms.go b/internal/pkg/encryption/keys/kms.go index f5108fe270c..c1695a06d91 100644 --- a/internal/pkg/encryption/keys/kms.go +++ b/internal/pkg/encryption/keys/kms.go @@ -13,9 +13,9 @@ import ( "io" "time" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/luks" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" + "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" diff --git a/internal/pkg/encryption/keys/nodeid.go b/internal/pkg/encryption/keys/nodeid.go index 6e85224fe47..d91b957e713 100644 --- a/internal/pkg/encryption/keys/nodeid.go +++ b/internal/pkg/encryption/keys/nodeid.go @@ -8,8 +8,8 @@ import ( "context" "fmt" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" + "github.com/siderolabs/go-blockdevice/v2/encryption" + "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" ) diff --git a/internal/pkg/encryption/keys/options.go b/internal/pkg/encryption/keys/options.go index 2f9a52f7f3c..c2dcd36b4b2 100644 --- a/internal/pkg/encryption/keys/options.go +++ b/internal/pkg/encryption/keys/options.go @@ -11,14 +11,14 @@ type KeyOption func(o *KeyOptions) error // KeyOptions set of options to be used in KeyHandler.GetKey func. type KeyOptions struct { - PartitionLabel string + VolumeID string GetSystemInformation helpers.SystemInformationGetter } -// WithPartitionLabel passes the partition label to the key handler. -func WithPartitionLabel(label string) KeyOption { +// WithVolumeID passes the partition label to the key handler. +func WithVolumeID(label string) KeyOption { return func(o *KeyOptions) error { - o.PartitionLabel = label + o.VolumeID = label return nil } diff --git a/internal/pkg/encryption/keys/static.go b/internal/pkg/encryption/keys/static.go index 53d976103e1..1585874ed31 100644 --- a/internal/pkg/encryption/keys/static.go +++ b/internal/pkg/encryption/keys/static.go @@ -7,8 +7,8 @@ package keys import ( "context" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" + "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. diff --git a/internal/pkg/encryption/keys/tpm2.go b/internal/pkg/encryption/keys/tpm2.go index 601c9d1fb51..e0c5af7deb7 100644 --- a/internal/pkg/encryption/keys/tpm2.go +++ b/internal/pkg/encryption/keys/tpm2.go @@ -12,9 +12,9 @@ import ( "io" "github.com/foxboron/go-uefi/efi" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/luks" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption/token" + "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/secureboot" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" diff --git a/internal/pkg/etcd/etcd.go b/internal/pkg/etcd/etcd.go index ebe25c4c52a..4589de59037 100644 --- a/internal/pkg/etcd/etcd.go +++ b/internal/pkg/etcd/etcd.go @@ -92,7 +92,7 @@ func NewClientFromControlPlaneIPs(ctx context.Context, resources state.State, di // ValidateForUpgrade validates the etcd cluster state to ensure that performing // an upgrade is safe. -func (c *Client) ValidateForUpgrade(ctx context.Context, config config.Config, preserve bool) error { +func (c *Client) ValidateForUpgrade(ctx context.Context, config config.Config) error { if config.Machine().Type() == machine.TypeWorker { return nil } @@ -102,12 +102,6 @@ func (c *Client) ValidateForUpgrade(ctx context.Context, config config.Config, p return err } - if !preserve { - if len(resp.Members) == 1 { - return errors.New("only 1 etcd member found; assuming this is not an HA setup and refusing to upgrade; if this is a single-node cluster, use --preserve to upgrade") - } - } - if len(resp.Members) == 2 { return fmt.Errorf("etcd member count(%d) is insufficient to maintain quorum if upgrade commences", len(resp.Members)) } diff --git a/internal/pkg/meta/meta.go b/internal/pkg/meta/meta.go index cb10e6b02ac..eb1b1d5f9d6 100644 --- a/internal/pkg/meta/meta.go +++ b/internal/pkg/meta/meta.go @@ -7,21 +7,25 @@ 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" - "github.com/siderolabs/go-blockdevice/blockdevice/probe" + 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" ) @@ -90,31 +94,54 @@ func New(ctx context.Context, st state.State, opts ...Option) (*Meta, error) { return meta, err } -func (meta *Meta) getPath() (string, error) { +func (meta *Meta) getPath(ctx context.Context) (string, string, error) { if meta.opts.fixedPath != "" { - return meta.opts.fixedPath, nil + return meta.opts.fixedPath, "", nil } - dev, err := probe.GetDevWithPartitionName(constants.MetaPartitionLabel) + if meta.state == nil { + return "", "", os.ErrNotExist + } + + metaStatus, err := block.WaitForVolumePhase(ctx, meta.state, constants.MetaPartitionLabel, block.VolumePhaseReady, block.VolumePhaseMissing) if err != nil { - return "", err + return "", "", err } - defer dev.Close() //nolint:errcheck + if metaStatus.TypedSpec().Phase != block.VolumePhaseReady { + return "", "", os.ErrNotExist + } - return dev.PartPath(constants.MetaPartitionLabel) + 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, err := meta.getPath() + 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 + } + f, err := os.Open(path) if err != nil { return err @@ -122,6 +149,10 @@ func (meta *Meta) Reload(ctx context.Context) error { 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 @@ -183,15 +214,32 @@ func (meta *Meta) syncState(ctx context.Context) error { } // Flush writes the META to the disk. +// +//nolint:gocyclo func (meta *Meta) Flush() error { meta.mu.Lock() defer meta.mu.Unlock() - path, err := meta.getPath() + 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 + } + f, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { return err @@ -199,6 +247,10 @@ func (meta *Meta) Flush() error { 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 @@ -343,3 +395,13 @@ func updateTagResource(ctx context.Context, st state.State, t uint8, val string) 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) + } +} diff --git a/internal/pkg/mount/system.go b/internal/pkg/mount/system.go index b0ccde996e8..4911a6d60f9 100644 --- a/internal/pkg/mount/system.go +++ b/internal/pkg/mount/system.go @@ -6,250 +6,66 @@ package mount import ( "context" - "errors" "fmt" "log" - "os" "sync" + "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/filesystem" - "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" - "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/disk" - "github.com/siderolabs/talos/internal/pkg/encryption" - "github.com/siderolabs/talos/internal/pkg/partition" - "github.com/siderolabs/talos/pkg/machinery/constants" + mountv2 "github.com/siderolabs/talos/internal/pkg/mount/v2" + "github.com/siderolabs/talos/pkg/machinery/resources/block" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) var ( - mountpoints = map[string]*Point{} + unmounters = map[string]func() error{} mountpointsMutex sync.RWMutex ) -// SystemMountPointsForDevice returns the mountpoints required to boot the system. -// This function is called exclusively during installations ( both image -// creation and bare metall installs ). This is why we want to look up -// device by specified disk as well as why we don't want to grow any -// filesystems. -func SystemMountPointsForDevice(ctx context.Context, devpath string, opts ...Option) (mountpoints *Points, err error) { - mountpoints = NewMountPoints() - - bd, err := blockdevice.Open(devpath) +// SystemPartitionMount mounts a system partition by the label. +func SystemPartitionMount(ctx context.Context, r runtime.Runtime, logger *log.Logger, label string, opts ...mountv2.NewPointOption) (err error) { + volumeStatus, err := safe.StateGetByID[*block.VolumeStatus](ctx, r.State().V1Alpha2().Resources(), label) if err != nil { - return nil, err + return fmt.Errorf("error getting volume status %q: %w", label, err) } - defer bd.Close() //nolint:errcheck - - for _, name := range []string{constants.EphemeralPartitionLabel, constants.BootPartitionLabel, constants.EFIPartitionLabel, constants.StatePartitionLabel} { - mountpoint, err := SystemMountPointForLabel(ctx, bd, name, opts...) - if err != nil { - return nil, err - } - - mountpoints.Set(name, mountpoint) + if volumeStatus.TypedSpec().Phase != block.VolumePhaseReady { + return fmt.Errorf("volume %q is not ready (%s)", label, volumeStatus.TypedSpec().Phase) } - return mountpoints, nil -} - -// SystemMountPointForLabel returns a mount point for the specified device and label. -// -//nolint:gocyclo -func SystemMountPointForLabel(ctx context.Context, device *blockdevice.BlockDevice, label string, opts ...Option) (mountpoint *Point, err error) { - var target string - - switch label { - case constants.EphemeralPartitionLabel: - target = constants.EphemeralMountPoint - case constants.BootPartitionLabel: - target = constants.BootMountPoint - case constants.EFIPartitionLabel: - target = constants.EFIMountPoint - case constants.StatePartitionLabel: - target = constants.StateMountPoint - default: - return nil, fmt.Errorf("unknown label: %q", label) - } - - part, err := device.GetPartition(label) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err - } - - if part == nil { - // A boot partitition is not required. - if label == constants.BootPartitionLabel { - return nil, nil - } - - return nil, fmt.Errorf("failed to find device with label %s: %w", label, err) - } - - fsType, err := part.Filesystem() + volumeConfig, err := safe.StateGetByID[*block.VolumeConfig](ctx, r.State().V1Alpha2().Resources(), label) if err != nil { - return nil, err + return fmt.Errorf("error getting volume config %q: %w", label, err) } - partPath, err := part.Path() - if err != nil { - return nil, err - } - - o := NewDefaultOptions(opts...) - - var preMountHooks []Hook - - if o.Encryption != nil { - encryptionHandler, err := encryption.NewHandler( - device, - part, - o.Encryption, - o.SystemInformationGetter, - ) - if err != nil { - return nil, err - } - - preMountHooks = append(preMountHooks, - func(p *Point) error { - var ( - err error - path string - ) - - if path, err = encryptionHandler.Open(ctx); err != nil { - return err - } - - p.source = path + mountpoint := mountv2.NewPoint( + volumeStatus.TypedSpec().MountLocation, + volumeConfig.TypedSpec().Mount.TargetPath, + volumeStatus.TypedSpec().Filesystem.String(), + opts..., + ) - return nil - }, - ) - - opts = append(opts, - WithPostUnmountHooks( - func(p *Point) error { - return encryptionHandler.Close() - }, - ), - ) - } - - // Format the partition if it does not have any filesystem - preMountHooks = append(preMountHooks, func(p *Point) error { - sb, err := filesystem.Probe(p.source) - if err != nil { - return err - } - - p.fstype = "" - - // skip formatting the partition if filesystem is detected - // and assign proper fs type to the mountpoint - if sb != nil && sb.Type() != filesystem.Unknown { - p.fstype = sb.Type() - - return nil - } - - opts := partition.NewFormatOptions(part.Name) - if opts == nil { - return fmt.Errorf("failed to determine format options for partition label %s", part.Name) - } - - if !o.MountFlags.Check(SkipIfNoFilesystem) { - p.fstype = opts.FileSystemType - - return partition.Format(p.source, opts, log.Printf) - } - - return nil - }) - - opts = append(opts, WithPreMountHooks(preMountHooks...)) - - mountpoint = NewMountPoint(partPath, target, fsType, unix.MS_NOATIME, "", opts...) - - return mountpoint, nil -} - -// SystemPartitionMount mounts a system partition by the label. -// -//nolint:gocyclo -func SystemPartitionMount(ctx context.Context, r runtime.Runtime, logger *log.Logger, label string, opts ...Option) (err error) { - device := r.State().Machine().Disk(disk.WithPartitionLabel(label)) - if device == nil { - return fmt.Errorf("failed to find device with partition labeled %s", label) - } - - if r.Config() != nil && r.Config().Machine() != nil { - encryptionConfig := r.Config().Machine().SystemDiskEncryption().Get(label) - - if encryptionConfig != nil { - opts = append(opts, - WithEncryptionConfig(encryptionConfig), - WithSystemInformationGetter(r.GetSystemInformation), - ) - } - } - - opts = append(opts, WithLogger(logger)) - - mountpoint, err := SystemMountPointForLabel(ctx, device.BlockDevice, label, opts...) + unmounter, err := mountpoint.Mount() if err != nil { return err } - if mountpoint == nil { - return fmt.Errorf("no mountpoints for label %q", label) - } - - var skipMount bool - - if skipMount, err = mountMountpoint(mountpoint); err != nil { - return err - } - - if skipMount { - if logger != nil { - logger.Printf("mount skipped") - } - - return - } - - o := NewDefaultOptions(opts...) - encrypted := o.Encryption != nil - // record mount as the resource mountStatus := runtimeres.NewMountStatus(v1alpha1.NamespaceName, label) - mountStatus.TypedSpec().Source = mountpoint.Source() - mountStatus.TypedSpec().Target = mountpoint.Target() - mountStatus.TypedSpec().FilesystemType = mountpoint.Fstype() - mountStatus.TypedSpec().Encrypted = encrypted + mountStatus.TypedSpec().Source = volumeStatus.TypedSpec().MountLocation + mountStatus.TypedSpec().Target = volumeConfig.TypedSpec().Mount.TargetPath + mountStatus.TypedSpec().FilesystemType = volumeStatus.TypedSpec().Filesystem.String() + mountStatus.TypedSpec().Encrypted = volumeStatus.TypedSpec().EncryptionProvider != block.EncryptionProviderNone - if encrypted { + if mountStatus.TypedSpec().Encrypted { encryptionProviders := make(map[string]struct{}) - for _, cfg := range o.Encryption.Keys() { - switch { - case cfg.Static() != nil: - encryptionProviders[cfg.Static().String()] = struct{}{} - case cfg.NodeID() != nil: - encryptionProviders[cfg.NodeID().String()] = struct{}{} - case cfg.KMS() != nil: - encryptionProviders[cfg.KMS().String()] = struct{}{} - case cfg.TPM() != nil: - encryptionProviders[cfg.TPM().String()] = struct{}{} - } + for _, cfg := range volumeConfig.TypedSpec().Encryption.Keys { + encryptionProviders[cfg.Type.String()] = struct{}{} } mountStatus.TypedSpec().EncryptionProviders = maps.Keys(encryptionProviders) @@ -263,7 +79,7 @@ func SystemPartitionMount(ctx context.Context, r runtime.Runtime, logger *log.Lo mountpointsMutex.Lock() defer mountpointsMutex.Unlock() - mountpoints[label] = mountpoint + unmounters[label] = unmounter return nil } @@ -271,7 +87,7 @@ func SystemPartitionMount(ctx context.Context, r runtime.Runtime, logger *log.Lo // SystemPartitionUnmount unmounts a system partition by the label. func SystemPartitionUnmount(r runtime.Runtime, logger *log.Logger, label string) (err error) { mountpointsMutex.RLock() - mountpoint, ok := mountpoints[label] + unmounter, ok := unmounters[label] mountpointsMutex.RUnlock() if !ok { @@ -282,7 +98,7 @@ func SystemPartitionUnmount(r runtime.Runtime, logger *log.Logger, label string) return nil } - err = mountpoint.Unmount() + err = unmounter() if err != nil { return err } @@ -292,7 +108,7 @@ func SystemPartitionUnmount(r runtime.Runtime, logger *log.Logger, label string) } mountpointsMutex.Lock() - delete(mountpoints, label) + delete(unmounters, label) mountpointsMutex.Unlock() return nil diff --git a/internal/pkg/mount/v2/mount.go b/internal/pkg/mount/v2/mount.go new file mode 100644 index 00000000000..a2e75b724b7 --- /dev/null +++ b/internal/pkg/mount/v2/mount.go @@ -0,0 +1,268 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "bufio" + "context" + "fmt" + "os" + "strings" + "time" + + "github.com/siderolabs/go-retry/retry" + "golang.org/x/sys/unix" +) + +// Point represents a mount point. +type Point struct { + source string + target string + fstype string + flags uintptr + data string +} + +// NewPointOption is a mount point option. +type NewPointOption func(*Point) + +// WithProjectQuota sets the project quota flag. +func WithProjectQuota(enabled bool) NewPointOption { + return func(p *Point) { + if !enabled { + return + } + + if len(p.data) > 0 { + p.data += "," + } + + p.data += "prjquota" + } +} + +// WithFlags sets the mount flags. +func WithFlags(flags uintptr) NewPointOption { + return func(p *Point) { + p.flags |= flags + } +} + +// WithReadonly sets the read-only flag. +func WithReadonly() NewPointOption { + return WithFlags(unix.MS_RDONLY) +} + +// NewPoint creates a new mount point. +func NewPoint(source, target, fstype string, opts ...NewPointOption) *Point { + p := &Point{ + source: source, + target: target, + fstype: fstype, + } + + for _, opt := range opts { + opt(p) + } + + return p +} + +// PrinterOptions are printer options. +type PrinterOptions struct { + Printer func(string, ...any) +} + +// Printf prints a formatted string (or skips if printer is nil). +func (o PrinterOptions) Printf(format string, args ...any) { + if o.Printer != nil { + o.Printer(format, args...) + } +} + +// OperationOptions are mount options. +type OperationOptions struct { + PrinterOptions + + SkipIfMounted bool + + TargetMode os.FileMode +} + +// OperationOption is a mount option. +type OperationOption func(*OperationOptions) + +// WithSkipIfMounted sets the skip if mounted flag. +func WithSkipIfMounted() OperationOption { + return func(o *OperationOptions) { + o.SkipIfMounted = true + } +} + +// WithMountPrinter sets the printer. +func WithMountPrinter(printer func(string, ...any)) OperationOption { + return func(o *OperationOptions) { + o.Printer = printer + } +} + +// UnmountOptions is unmount options. +type UnmountOptions struct { + PrinterOptions +} + +// UnmountOption is an unmount option. +type UnmountOption func(*UnmountOptions) + +// WithUnmountPrinter sets the printer. +func WithUnmountPrinter(printer func(string, ...any)) UnmountOption { + return func(o *UnmountOptions) { + o.Printer = printer + } +} + +// 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() +} + +// Mount the mount point. +// +// Mount returns an unmounter function to unmount the mount point. +func (p *Point) Mount(opts ...OperationOption) (unmounter func() error, err error) { + options := OperationOptions{ + TargetMode: 0o755, + } + + for _, opt := range opts { + opt(&options) + } + + if options.SkipIfMounted { + isMounted, err := p.IsMounted() + if err != nil { + return nil, err + } + + // already mounted, return a no-op unmounter + if isMounted { + return func() error { + return nil + }, nil + } + } + + if err = os.MkdirAll(p.target, options.TargetMode); err != nil { + return nil, fmt.Errorf("error creating mount point directory %s: %w", p.target, err) + } + + err = p.retry(p.mount, false, options.PrinterOptions) + if err != nil { + return nil, fmt.Errorf("error mounting %s: %w", p.source, err) + } + + return func() error { + return p.Unmount(WithUnmountPrinter(options.Printer)) + }, nil +} + +// Unmount the mount point. +func (p *Point) Unmount(opts ...UnmountOption) error { + var options UnmountOptions + + for _, opt := range opts { + opt(&options) + } + + mounted, err := p.IsMounted() + if err != nil { + return err + } + + if !mounted { + return nil + } + + return p.retry(func() error { + return p.unmount(options.Printer) + }, true, options.PrinterOptions) +} + +func (p *Point) mount() error { + return unix.Mount(p.source, p.target, p.fstype, p.flags, p.data) +} + +func (p *Point) unmount(printer func(string, ...any)) error { + return SafeUnmount(context.Background(), printer, p.target) +} + +//nolint:gocyclo +func (p *Point) retry(f func() error, isUnmount bool, printerOptions PrinterOptions) 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.repair(printerOptions); 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.repair(printerOptions); 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 + }) +} diff --git a/internal/pkg/mount/v2/points.go b/internal/pkg/mount/v2/points.go new file mode 100644 index 00000000000..4232bbdf256 --- /dev/null +++ b/internal/pkg/mount/v2/points.go @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + "slices" +) + +// Points is a list of mount points. +type Points []*Point + +// Mount all mount points. +func (points Points) Mount(opts ...OperationOption) (unmounter func() error, err error) { + unmounters := make([]func() error, 0, len(points)) + + for _, point := range points { + unmounter, err := point.Mount(opts...) + if err != nil { + // unmount what got already mounted + slices.Reverse(unmounters) + + for _, unmounter := range unmounters { + _ = unmounter() //nolint:errcheck + } + + return nil, err + } + + unmounters = append(unmounters, unmounter) + } + + slices.Reverse(unmounters) + + return func() error { + var unmountErr error + + for _, unmounter := range unmounters { + unmountErr = errors.Join(unmounter()) + } + + return unmountErr + }, nil +} + +// Unmount all mount points. +func (points Points) Unmount() error { + for i := len(points) - 1; i >= 0; i-- { + if err := points[i].Unmount(); err != nil { + return err + } + } + + return nil +} diff --git a/internal/pkg/mount/v2/repair.go b/internal/pkg/mount/v2/repair.go new file mode 100644 index 00000000000..06ffacad90e --- /dev/null +++ b/internal/pkg/mount/v2/repair.go @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + "github.com/siderolabs/talos/pkg/makefs" +) + +// repair a filesystem. +func (p *Point) repair(printerOptions PrinterOptions) error { + printerOptions.Printf("filesystem on %s needs cleaning, running repair", p.source) + + if err := makefs.XFSRepair(p.source, p.fstype); err != nil { + return fmt.Errorf("xfs_repair: %w", err) + } + + printerOptions.Printf("filesystem successfully repaired on %s", p.source) + + return nil +} diff --git a/internal/pkg/mount/v2/unmount.go b/internal/pkg/mount/v2/unmount.go new file mode 100644 index 00000000000..ef6c8904272 --- /dev/null +++ b/internal/pkg/mount/v2/unmount.go @@ -0,0 +1,76 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 unmountLoop(ctx context.Context, printer func(string, ...any), target string, flags int, timeout time.Duration, extraMessage string) (bool, error) { + errCh := make(chan error, 1) + + 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 + } + + if printer != nil { + 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. +func SafeUnmount(ctx context.Context, printer func(string, ...any), target string) error { + const ( + unmountTimeout = 90 * time.Second + unmountForceTimeout = 10 * time.Second + ) + + ok, err := unmountLoop(ctx, printer, target, 0, unmountTimeout, "") + + if ok { + return err + } + + if printer != nil { + printer("unmounting %s with force", target) + } + + ok, err = unmountLoop(ctx, printer, target, unix.MNT_FORCE, unmountTimeout, " with force flag") + + if ok { + return err + } + + return fmt.Errorf("unmounting %s with force flag timed out", target) +} diff --git a/internal/pkg/partition/constants.go b/internal/pkg/partition/constants.go index 53453718196..0f2c8d17f99 100644 --- a/internal/pkg/partition/constants.go +++ b/internal/pkg/partition/constants.go @@ -21,14 +21,16 @@ type FileSystemType = string // Filesystem types. const ( - FilesystemTypeNone FileSystemType = "none" - FilesystemTypeXFS FileSystemType = "xfs" - FilesystemTypeVFAT FileSystemType = "vfat" + FilesystemTypeNone FileSystemType = "none" + FilesystemTypeZeroes FileSystemType = "zeroes" + FilesystemTypeXFS FileSystemType = "xfs" + FilesystemTypeVFAT FileSystemType = "vfat" ) // Partition default sizes. const ( MiB = 1024 * 1024 + GiB = 1024 * MiB EFISize = 100 * MiB BIOSGrubSize = 1 * MiB @@ -36,7 +38,8 @@ const ( // 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. - EFIUKISize = EFISize + BIOSGrubSize + BootSize - MetaSize = 1 * MiB - StateSize = 100 * MiB + EFIUKISize = EFISize + BIOSGrubSize + BootSize + MetaSize = 1 * MiB + StateSize = 100 * MiB + EphemeralMinSize = 2 * GiB ) diff --git a/internal/pkg/partition/format.go b/internal/pkg/partition/format.go index 8c59742ddbc..0dd1319afe6 100644 --- a/internal/pkg/partition/format.go +++ b/internal/pkg/partition/format.go @@ -8,7 +8,7 @@ package partition import ( "fmt" - "github.com/siderolabs/go-blockdevice/blockdevice" + "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/makefs" @@ -29,10 +29,6 @@ func NewFormatOptions(label string) *FormatOptions { // Format zeroes the device and formats it using filesystem type provided. func Format(devname string, t *FormatOptions, printf func(string, ...any)) error { - if t.FileSystemType == FilesystemTypeNone { - return zeroPartition(devname, printf) - } - opts := []makefs.Option{makefs.WithForce(t.Force), makefs.WithLabel(t.Label)} if t.UnsupportedFSOption { @@ -42,6 +38,10 @@ func Format(devname string, t *FormatOptions, printf func(string, ...any)) error 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(devname, opts...) case FilesystemTypeXFS: @@ -52,10 +52,8 @@ func Format(devname string, t *FormatOptions, printf func(string, ...any)) error } // zeroPartition fills the partition with zeroes. -func zeroPartition(devname string, printf func(string, ...any)) (err error) { - printf("zeroing out %q", devname) - - part, err := blockdevice.Open(devname, blockdevice.WithExclusiveLock(true)) +func zeroPartition(devname string) (err error) { + part, err := block.NewFromPath(devname, block.OpenForWrite()) if err != nil { return err } @@ -76,7 +74,7 @@ func systemPartitionsFormatOptions(label string) *FormatOptions { case constants.BIOSGrubPartitionLabel: return &FormatOptions{ Label: constants.BIOSGrubPartitionLabel, - FileSystemType: FilesystemTypeNone, + FileSystemType: FilesystemTypeZeroes, Force: true, } case constants.BootPartitionLabel: @@ -88,21 +86,18 @@ func systemPartitionsFormatOptions(label string) *FormatOptions { case constants.MetaPartitionLabel: return &FormatOptions{ Label: constants.MetaPartitionLabel, - FileSystemType: FilesystemTypeNone, + FileSystemType: FilesystemTypeZeroes, Force: true, } case constants.StatePartitionLabel: return &FormatOptions{ - Label: constants.StatePartitionLabel, - FileSystemType: FilesystemTypeXFS, - Force: true, - UnsupportedFSOption: true, + Label: constants.StatePartitionLabel, + FileSystemType: FilesystemTypeNone, } case constants.EphemeralPartitionLabel: return &FormatOptions{ Label: constants.EphemeralPartitionLabel, - FileSystemType: FilesystemTypeXFS, - Force: true, + FileSystemType: FilesystemTypeNone, } default: return nil diff --git a/internal/pkg/partition/format_test.go b/internal/pkg/partition/format_test.go deleted file mode 100644 index 79ffe22f1f7..00000000000 --- a/internal/pkg/partition/format_test.go +++ /dev/null @@ -1,157 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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_test - -import ( - "bytes" - "io" - "os" - "testing" - - "github.com/siderolabs/go-blockdevice/blockdevice" - "github.com/siderolabs/go-blockdevice/blockdevice/loopback" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" - "github.com/stretchr/testify/suite" - - "github.com/siderolabs/talos/internal/pkg/partition" -) - -type manifestSuite struct { - suite.Suite - - disk *os.File - loopbackDevice *os.File -} - -const ( - diskSize = 10 * 1024 * 1024 // 10 MiB -) - -func TestManifestSuite(t *testing.T) { - suite.Run(t, new(manifestSuite)) -} - -func (suite *manifestSuite) SetupTest() { - suite.skipIfNotRoot() - - var err error - - suite.disk, err = os.CreateTemp("", "talos") - suite.Require().NoError(err) - - suite.Require().NoError(suite.disk.Truncate(diskSize)) - - suite.loopbackDevice, err = loopback.NextLoopDevice() - suite.Require().NoError(err) - - suite.T().Logf("Using %s", suite.loopbackDevice.Name()) - - suite.Require().NoError(loopback.Loop(suite.loopbackDevice, suite.disk)) - - suite.Require().NoError(loopback.LoopSetReadWrite(suite.loopbackDevice)) -} - -func (suite *manifestSuite) TearDownTest() { - if suite.loopbackDevice != nil { - suite.Assert().NoError(loopback.Unloop(suite.loopbackDevice)) - } - - if suite.disk != nil { - suite.Assert().NoError(os.Remove(suite.disk.Name())) - suite.Assert().NoError(suite.disk.Close()) - } -} - -func (suite *manifestSuite) skipIfNotRoot() { - if os.Getuid() != 0 { - suite.T().Skip("can't run the test as non-root") - } -} - -func (suite *manifestSuite) skipUnderBuildkit() { - hostname, _ := os.Hostname() //nolint:errcheck - - if hostname == "buildkitsandbox" { - suite.T().Skip("test not supported under buildkit as partition devices are not propagated from /dev") - } -} - -func (suite *manifestSuite) TestZeroPartition() { - suite.skipUnderBuildkit() - - bd, err := blockdevice.Open(suite.loopbackDevice.Name(), blockdevice.WithExclusiveLock(true)) - suite.Require().NoError(err) - - defer bd.Close() //nolint:errcheck - - pt, err := gpt.New(bd.Device(), gpt.WithMarkMBRBootable(false)) - suite.Require().NoError(err) - - // Create a partition table with a single partition. - _, err = pt.Add(0, gpt.WithMaximumSize(true), gpt.WithPartitionName("zerofill")) - suite.Require().NoError(err) - - suite.Require().NoError(pt.Write()) - suite.Require().NoError(bd.Close()) - - bd, err = blockdevice.Open(suite.loopbackDevice.Name(), blockdevice.WithExclusiveLock(true)) - suite.Require().NoError(err) - - defer bd.Close() //nolint:errcheck - - fills := bytes.NewBuffer(bytes.Repeat([]byte{1}, 10)) - - parts, err := bd.GetPartition("zerofill") - suite.Require().NoError(err) - - part, err := parts.Path() - suite.Require().NoError(err) - - // open the partition as read write - dst, err := os.OpenFile(part, os.O_WRONLY, 0o644) - suite.Require().NoError(err) - - defer dst.Close() //nolint:errcheck - - // Write some data to the partition. - _, err = io.Copy(dst, fills) - suite.Require().NoError(err) - - data, err := os.Open(part) - suite.Require().NoError(err) - - defer data.Close() //nolint:errcheck - - read := make([]byte, fills.Len()) - - _, err = data.Read(read) - suite.Require().NoError(err) - suite.Require().NoError(data.Close()) - - suite.Assert().True(bytes.Equal(fills.Bytes(), read)) - - suite.Require().NoError(bd.Close()) - - err = partition.Format(part, &partition.FormatOptions{ - FileSystemType: partition.FilesystemTypeNone, - }, suite.T().Logf) - suite.Require().NoError(err) - - // reading 10 times more than what we wrote should still return 0 since the partition is wiped - zerofills := bytes.NewBuffer(bytes.Repeat([]byte{0}, 100)) - - data, err = os.Open(part) - suite.Require().NoError(err) - - defer data.Close() //nolint:errcheck - - read = make([]byte, zerofills.Len()) - - _, err = data.Read(read) - suite.Require().NoError(err) - - suite.Assert().True(bytes.Equal(zerofills.Bytes(), read)) -} diff --git a/internal/pkg/partition/partition.go b/internal/pkg/partition/partition.go index fcafa687a93..84ab99c898b 100644 --- a/internal/pkg/partition/partition.go +++ b/internal/pkg/partition/partition.go @@ -6,75 +6,39 @@ package partition import ( - "github.com/dustin/go-humanize" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" + "fmt" + + "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Options contains the options for creating a partition. type Options struct { - PartitionLabel string - PartitionType Type - Size uint64 - LegacyBIOSBootable bool -} + FormatOptions -// NewPartitionOptions returns a new PartitionOptions. -func NewPartitionOptions(label string, uki bool) *Options { - return systemPartitionsPartitonOptions(label, uki) + PartitionLabel string + PartitionType Type + Size uint64 + PartitionOpts []gpt.PartitionOption } -// Locate existing partition on the disk by label. -func Locate(pt *gpt.GPT, label string) (*gpt.Partition, error) { - for _, part := range pt.Partitions().Items() { - if part.Name == label { - return part, nil - } - } - - return nil, nil -} - -// Partition creates a new partition on the specified device. -// Returns the path to the newly created partition. -func Partition(pt *gpt.GPT, pos int, device string, partitionOpts Options, printf func(string, ...any)) (string, error) { - printf("partitioning %s - %s %q\n", device, partitionOpts.PartitionLabel, humanize.Bytes(partitionOpts.Size)) - - opts := []gpt.PartitionOption{ - gpt.WithPartitionType(partitionOpts.PartitionType), - gpt.WithPartitionName(partitionOpts.PartitionLabel), - } - - if partitionOpts.Size == 0 { - opts = append(opts, gpt.WithMaximumSize(true)) - } - - if partitionOpts.LegacyBIOSBootable { - opts = append(opts, gpt.WithLegacyBIOSBootableAttribute(true)) - } - - part, err := pt.InsertAt(pos, partitionOpts.Size, opts...) - if err != nil { - return "", err - } - - partitionName, err := part.Path() - if err != nil { - return "", err +// NewPartitionOptions returns a new PartitionOptions. +// +//nolint:gocyclo +func NewPartitionOptions(label string, uki bool) Options { + formatOptions := NewFormatOptions(label) + if formatOptions == nil { + panic(fmt.Sprintf("unknown format options for label %q", label)) } - printf("created %s (%s) size %d blocks", partitionName, partitionOpts.PartitionLabel, part.Length()) - - return partitionName, nil -} - -func systemPartitionsPartitonOptions(label string, uki bool) *Options { switch label { case constants.EFIPartitionLabel: - partitionOptions := &Options{ - PartitionType: EFISystemPartition, - Size: EFISize, + partitionOptions := Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: EFISystemPartition, + Size: EFISize, } if uki { @@ -87,35 +51,46 @@ func systemPartitionsPartitonOptions(label string, uki bool) *Options { panic("BIOS partition is not supported with UKI") } - return &Options{ - PartitionType: BIOSBootPartition, - Size: BIOSGrubSize, + return Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: BIOSBootPartition, + Size: BIOSGrubSize, + PartitionOpts: []gpt.PartitionOption{gpt.WithLegacyBIOSBootableAttribute(true)}, } case constants.BootPartitionLabel: if uki { panic("BOOT partition is not supported with UKI") } - return &Options{ - PartitionType: LinuxFilesystemData, - Size: BootSize, + return Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: LinuxFilesystemData, + Size: BootSize, } case constants.MetaPartitionLabel: - return &Options{ - PartitionType: LinuxFilesystemData, - Size: MetaSize, + return Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: LinuxFilesystemData, + Size: MetaSize, } case constants.StatePartitionLabel: - return &Options{ - PartitionType: LinuxFilesystemData, - Size: StateSize, + return Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: LinuxFilesystemData, + Size: StateSize, } case constants.EphemeralPartitionLabel: - return &Options{ - PartitionType: LinuxFilesystemData, - Size: 0, + return Options{ + FormatOptions: *formatOptions, + PartitionLabel: label, + PartitionType: LinuxFilesystemData, + Size: 0, } default: - return nil + panic(fmt.Sprintf("unknown partition label %q", label)) } } diff --git a/internal/pkg/partition/wipe.go b/internal/pkg/partition/wipe.go new file mode 100644 index 00000000000..21d84c72b88 --- /dev/null +++ b/internal/pkg/partition/wipe.go @@ -0,0 +1,58 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" +) + +// VolumeWipeTarget is a target for wiping a volume. +type VolumeWipeTarget struct { + VolumeStatus *blockres.VolumeStatus +} + +// GetLabel implements runtime.PartitionTarget. +func (v *VolumeWipeTarget) GetLabel() string { + return v.VolumeStatus.Metadata().ID() +} + +// Wipe implements runtime.PartitionTarget. +func (v *VolumeWipeTarget) Wipe(ctx context.Context, log func(string, ...any)) error { + parentDevName := v.VolumeStatus.TypedSpec().ParentLocation + + if parentDevName == "" { + parentDevName = v.VolumeStatus.TypedSpec().Location + } + + parentBd, err := block.NewFromPath(parentDevName) + if err != nil { + return fmt.Errorf("error opening block device %q: %w", 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", parentDevName, err) + } + + defer parentBd.Unlock() //nolint:errcheck + + bd, err := block.NewFromPath(v.VolumeStatus.TypedSpec().Location, block.OpenForWrite()) + if err != nil { + return fmt.Errorf("error opening block device %q: %w", v.VolumeStatus.TypedSpec().Location, err) + } + + defer bd.Close() //nolint:errcheck + + log("wiping the volume %q (%s)", v.GetLabel(), v.VolumeStatus.TypedSpec().Location) + + return bd.FastWipe() +} diff --git a/pkg/imager/out.go b/pkg/imager/out.go index 14338ef4d6d..3dd32245eb6 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -7,13 +7,16 @@ package imager import ( "context" "encoding/pem" + "errors" "fmt" "log" + randv2 "math/rand/v2" "os" "path/filepath" "strings" "time" + "github.com/freddierice/go-losetup/v2" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" @@ -22,6 +25,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/types" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" + "golang.org/x/sys/unix" "gopkg.in/yaml.v3" "github.com/siderolabs/talos/cmd/installer/pkg/install" @@ -227,18 +231,35 @@ func (i *Imager) buildImage(ctx context.Context, path string, printf func(string printf("attaching loopback device") var ( - loDevice string + loDevice losetup.Device err error ) - if loDevice, err = utils.Loattach(path); err != nil { - return err + for range 10 { + loDevice, err = losetup.Attach(path, 0, false) + if err != nil { + if errors.Is(err, unix.EBUSY) { + spraySleep := max(randv2.ExpFloat64(), 2.0) + + printf("retrying after %v seconds", spraySleep) + + time.Sleep(time.Duration(spraySleep * float64(time.Second))) + + continue + } + + return fmt.Errorf("failed to attach loopback device: %w", err) + } + + printf("attached loopback device: %s", loDevice.Path()) + + break } defer func() { - printf("detaching loopback device") + printf("detaching loopback device %s", loDevice.Path()) - if e := utils.Lodetach(loDevice); e != nil { + if e := loDevice.Detach(); e != nil { log.Println(e) } }() @@ -248,7 +269,7 @@ func (i *Imager) buildImage(ctx context.Context, path string, printf func(string scratchSpace := filepath.Join(i.tempDir, "image") opts := &install.Options{ - Disk: loDevice, + Disk: loDevice.Path(), Platform: i.prof.Platform, Arch: i.prof.Arch, Board: i.prof.Board, diff --git a/pkg/imager/utils/loopback.go b/pkg/imager/utils/loopback.go deleted file mode 100644 index e771869bc97..00000000000 --- a/pkg/imager/utils/loopback.go +++ /dev/null @@ -1,30 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. 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 - -import ( - "fmt" - "strings" - - "github.com/siderolabs/go-cmd/pkg/cmd" -) - -// Loattach attaches a loopback device by inoking the `losetup` command. -func Loattach(img string) (dev string, err error) { - if dev, err = cmd.Run("losetup", "--find", "--partscan", "--nooverlap", "--show", img); err != nil { - return "", fmt.Errorf("failed to setup loopback device: %w", err) - } - - return strings.TrimSuffix(dev, "\n"), nil -} - -// Lodetach detaches a loopback device by inoking the `losetup` command. -func Lodetach(img string) (err error) { - if _, err = cmd.Run("losetup", "-d", img); err != nil { - return fmt.Errorf("failed to detach loopback device: %w", err) - } - - return nil -} diff --git a/pkg/machinery/api/resource/definitions/block/block.pb.go b/pkg/machinery/api/resource/definitions/block/block.pb.go index 3241154642c..32e0d3d7a88 100644 --- a/pkg/machinery/api/resource/definitions/block/block.pb.go +++ b/pkg/machinery/api/resource/definitions/block/block.pb.go @@ -10,8 +10,11 @@ import ( reflect "reflect" sync "sync" + 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 ( @@ -147,6 +150,8 @@ type DiscoveredVolumeSpec struct { 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"` } func (x *DiscoveredVolumeSpec) Reset() { @@ -293,6 +298,164 @@ func (x *DiscoveredVolumeSpec) GetParent() string { 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 "" +} + +// DiscoveryRefreshRequestSpec is the spec for DiscoveryRefreshRequest. +type DiscoveryRefreshRequestSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request int64 `protobuf:"varint,1,opt,name=request,proto3" json:"request,omitempty"` +} + +func (x *DiscoveryRefreshRequestSpec) Reset() { + *x = DiscoveryRefreshRequestSpec{} + if protoimpl.UnsafeEnabled { + 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 protoimpl.UnsafeEnabled && 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 + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request int64 `protobuf:"varint,1,opt,name=request,proto3" json:"request,omitempty"` +} + +func (x *DiscoveryRefreshStatusSpec) Reset() { + *x = DiscoveryRefreshStatusSpec{} + if protoimpl.UnsafeEnabled { + 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 protoimpl.UnsafeEnabled && 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 + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Match *v1alpha1.CheckedExpr `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` +} + +func (x *DiskSelector) Reset() { + *x = DiskSelector{} + if protoimpl.UnsafeEnabled { + 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 protoimpl.UnsafeEnabled && 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 +} + // DiskSpec is the spec for Disks status. type DiskSpec struct { state protoimpl.MessageState @@ -312,12 +475,13 @@ type DiskSpec struct { 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"` } func (x *DiskSpec) Reset() { *x = DiskSpec{} if protoimpl.UnsafeEnabled { - mi := &file_resource_definitions_block_block_proto_msgTypes[2] + mi := &file_resource_definitions_block_block_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -330,7 +494,7 @@ func (x *DiskSpec) String() string { func (*DiskSpec) ProtoMessage() {} func (x *DiskSpec) ProtoReflect() protoreflect.Message { - mi := &file_resource_definitions_block_block_proto_msgTypes[2] + mi := &file_resource_definitions_block_block_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -343,7 +507,7 @@ func (x *DiskSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use DiskSpec.ProtoReflect.Descriptor instead. func (*DiskSpec) Descriptor() ([]byte, []int) { - return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{2} + return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{5} } func (x *DiskSpec) GetSize() uint64 { @@ -437,32 +601,43 @@ func (x *DiskSpec) GetCdrom() bool { return false } -// SystemDiskSpec is the spec for SystemDisks status. -type SystemDiskSpec struct { +func (x *DiskSpec) GetDevPath() string { + if x != nil { + return x.DevPath + } + return "" +} + +// EncryptionKey is the spec for volume encryption key. +type EncryptionKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DiskId string `protobuf:"bytes,1,opt,name=disk_id,json=diskId,proto3" json:"disk_id,omitempty"` + 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"` } -func (x *SystemDiskSpec) Reset() { - *x = SystemDiskSpec{} +func (x *EncryptionKey) Reset() { + *x = EncryptionKey{} if protoimpl.UnsafeEnabled { - mi := &file_resource_definitions_block_block_proto_msgTypes[3] + mi := &file_resource_definitions_block_block_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SystemDiskSpec) String() string { +func (x *EncryptionKey) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SystemDiskSpec) ProtoMessage() {} +func (*EncryptionKey) ProtoMessage() {} -func (x *SystemDiskSpec) ProtoReflect() protoreflect.Message { - mi := &file_resource_definitions_block_block_proto_msgTypes[3] +func (x *EncryptionKey) ProtoReflect() protoreflect.Message { + mi := &file_resource_definitions_block_block_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -473,153 +648,1112 @@ func (x *SystemDiskSpec) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SystemDiskSpec.ProtoReflect.Descriptor instead. -func (*SystemDiskSpec) Descriptor() ([]byte, []int) { - return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{3} +// Deprecated: Use EncryptionKey.ProtoReflect.Descriptor instead. +func (*EncryptionKey) Descriptor() ([]byte, []int) { + return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{6} } -func (x *SystemDiskSpec) GetDiskId() string { +func (x *EncryptionKey) GetSlot() int64 { if x != nil { - return x.DiskId + 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 "" } -var File_resource_definitions_block_block_proto protoreflect.FileDescriptor +func (x *EncryptionKey) GetTpmCheckSecurebootStatusOnEnroll() bool { + if x != nil { + return x.TpmCheckSecurebootStatusOnEnroll + } + return false +} -var file_resource_definitions_block_block_proto_rawDesc = []byte{ - 0x0a, 0x26, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x20, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6d, 0x61, - 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x72, - 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x22, 0x83, 0x04, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, - 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x6f, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x69, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, - 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, - 0x70, 0x72, 0x6f, 0x62, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x25, 0x0a, - 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x55, 0x75, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, - 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x61, 0x62, 0x65, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70, - 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x22, 0xe0, 0x02, 0x0a, 0x08, 0x44, - 0x69, 0x73, 0x6b, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, - 0x6f, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x69, 0x6f, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, - 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x77, - 0x77, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77, 0x77, 0x69, 0x64, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x62, 0x75, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x75, - 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x64, 0x72, 0x6f, 0x6d, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x64, 0x72, 0x6f, 0x6d, 0x22, 0x29, 0x0a, - 0x0e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x70, 0x65, 0x63, 0x12, - 0x17, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x64, 0x42, 0x74, 0x0a, 0x28, 0x64, 0x65, 0x76, 0x2e, - 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, - 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +// EncryptionSpec is the spec for volume encryption. +type EncryptionSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` } -var ( - file_resource_definitions_block_block_proto_rawDescOnce sync.Once - file_resource_definitions_block_block_proto_rawDescData = file_resource_definitions_block_block_proto_rawDesc -) +func (x *EncryptionSpec) Reset() { + *x = EncryptionSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -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(file_resource_definitions_block_block_proto_rawDescData) - }) - return file_resource_definitions_block_block_proto_rawDescData +func (x *EncryptionSpec) String() string { + return protoimpl.X.MessageStringOf(x) } -var file_resource_definitions_block_block_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -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 - (*DiskSpec)(nil), // 2: talos.resource.definitions.block.DiskSpec - (*SystemDiskSpec)(nil), // 3: talos.resource.definitions.block.SystemDiskSpec +func (*EncryptionSpec) ProtoMessage() {} + +func (x *EncryptionSpec) ProtoReflect() protoreflect.Message { + mi := &file_resource_definitions_block_block_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var file_resource_definitions_block_block_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 + +// Deprecated: Use EncryptionSpec.ProtoReflect.Descriptor instead. +func (*EncryptionSpec) Descriptor() ([]byte, []int) { + return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{7} } -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 +func (x *EncryptionSpec) GetProvider() enums.BlockEncryptionProviderType { + if x != nil { + return x.Provider } - if !protoimpl.UnsafeEnabled { - file_resource_definitions_block_block_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*DeviceSpec); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } + 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 + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *FilesystemSpec) Reset() { + *x = FilesystemSpec{} + if protoimpl.UnsafeEnabled { + 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 protoimpl.UnsafeEnabled && 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 + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Match *v1alpha1.CheckedExpr `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` +} + +func (x *LocatorSpec) Reset() { + *x = LocatorSpec{} + if protoimpl.UnsafeEnabled { + 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 protoimpl.UnsafeEnabled && 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 +} + +// MountSpec is the spec for volume mount. +type MountSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TargetPath string `protobuf:"bytes,1,opt,name=target_path,json=targetPath,proto3" json:"target_path,omitempty"` +} + +func (x *MountSpec) Reset() { + *x = MountSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[10] + 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[10] + if protoimpl.UnsafeEnabled && 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{10} +} + +func (x *MountSpec) GetTargetPath() string { + if x != nil { + return x.TargetPath + } + return "" +} + +// PartitionSpec is the spec for volume partitioning. +type PartitionSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *PartitionSpec) Reset() { + *x = PartitionSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[11] + 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[11] + if protoimpl.UnsafeEnabled && 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{11} +} + +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 "" +} + +// ProvisioningSpec is the spec for volume provisioning. +type ProvisioningSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *ProvisioningSpec) Reset() { + *x = ProvisioningSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[12] + 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[12] + if protoimpl.UnsafeEnabled && 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{12} +} + +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 +} + +// SystemDiskSpec is the spec for SystemDisks status. +type SystemDiskSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *SystemDiskSpec) Reset() { + *x = SystemDiskSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[13] + 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[13] + if protoimpl.UnsafeEnabled && 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{13} +} + +func (x *SystemDiskSpec) GetDiskId() string { + if x != nil { + return x.DiskId + } + return "" +} + +func (x *SystemDiskSpec) GetDevPath() string { + if x != nil { + return x.DevPath + } + return "" +} + +// UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus status. +type UserDiskConfigStatusSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` +} + +func (x *UserDiskConfigStatusSpec) Reset() { + *x = UserDiskConfigStatusSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[14] + 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[14] + if protoimpl.UnsafeEnabled && 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{14} +} + +func (x *UserDiskConfigStatusSpec) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +// VolumeConfigSpec is the spec for VolumeConfig resource. +type VolumeConfigSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *VolumeConfigSpec) Reset() { + *x = VolumeConfigSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[15] + 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[15] + if protoimpl.UnsafeEnabled && 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{15} +} + +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 +} + +// VolumeStatusSpec is the spec for VolumeStatus resource. +type VolumeStatusSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + 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"` +} + +func (x *VolumeStatusSpec) Reset() { + *x = VolumeStatusSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_resource_definitions_block_block_proto_msgTypes[16] + 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[16] + if protoimpl.UnsafeEnabled && 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{16} +} + +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) +} + +var File_resource_definitions_block_block_proto protoreflect.FileDescriptor + +var file_resource_definitions_block_block_proto_rawDesc = []byte{ + 0x0a, 0x26, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x20, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x26, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x70, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x26, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2f, 0x65, + 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6d, 0x61, + 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x67, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x22, 0xc6, 0x04, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x6f, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x69, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x62, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x25, 0x0a, + 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x55, 0x75, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x65, + 0x76, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, + 0x76, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x64, 0x65, 0x76, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x76, 0x50, 0x61, 0x74, 0x68, 0x22, 0x37, 0x0a, + 0x1b, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x36, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4b, + 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x3b, + 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, + 0x45, 0x78, 0x70, 0x72, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0xfb, 0x02, 0x0a, 0x08, + 0x44, 0x69, 0x73, 0x6b, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x17, 0x0a, 0x07, + 0x69, 0x6f, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x69, + 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, + 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, + 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x77, 0x77, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77, 0x77, 0x69, 0x64, + 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x64, 0x72, 0x6f, + 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x64, 0x72, 0x6f, 0x6d, 0x12, 0x19, + 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x64, 0x65, 0x76, 0x50, 0x61, 0x74, 0x68, 0x22, 0x92, 0x02, 0x0a, 0x0d, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, + 0x4c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, + 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, + 0x11, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, + 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x6d, + 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x6b, 0x6d, 0x73, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4f, 0x0a, + 0x25, 0x74, 0x70, 0x6d, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, + 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6f, 0x6e, 0x5f, + 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x20, 0x74, 0x70, + 0x6d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4f, 0x6e, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x22, 0xa5, + 0x02, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, + 0x63, 0x12, 0x59, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x3d, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x04, + 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x61, 0x6c, + 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6b, 0x65, 0x79, + 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x66, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x66, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x71, 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x53, 0x70, 0x65, 0x63, 0x12, 0x49, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x4a, 0x0a, 0x0b, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x3b, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x45, 0x78, 0x70, 0x72, 0x52, 0x05, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x2c, 0x0a, 0x09, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, + 0x61, 0x74, 0x68, 0x22, 0x8c, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x67, + 0x72, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x67, 0x72, 0x6f, 0x77, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x55, 0x75, + 0x69, 0x64, 0x22, 0xae, 0x02, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x69, 0x6e, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x53, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x6b, 0x5f, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, + 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, + 0x64, 0x69, 0x73, 0x6b, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x0e, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x77, 0x61, 0x76, 0x65, 0x12, 0x59, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, + 0x70, 0x65, 0x63, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, + 0x70, 0x65, 0x63, 0x22, 0x44, 0x0a, 0x0e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x69, 0x73, + 0x6b, 0x53, 0x70, 0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x64, 0x65, 0x76, 0x50, 0x61, 0x74, 0x68, 0x22, 0x30, 0x0a, 0x18, 0x55, 0x73, 0x65, + 0x72, 0x44, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x22, 0xac, 0x03, 0x0a, 0x10, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, + 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x45, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x74, 0x61, + 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x56, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x61, 0x6c, + 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0c, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x47, 0x0a, 0x07, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, + 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x07, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x41, 0x0a, 0x05, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x05, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, + 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0a, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x05, 0x0a, 0x10, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, + 0x48, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, + 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, + 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x50, 0x68, 0x61, + 0x73, 0x65, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x75, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x55, 0x75, 0x69, 0x64, 0x12, 0x58, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x5f, 0x66, 0x61, 0x69, + 0x6c, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, + 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x50, 0x68, 0x61, 0x73, + 0x65, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x55, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x74, 0x61, 0x6c, 0x6f, + 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x25, 0x0a, 0x0e, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x6e, 0x0a, 0x13, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x3d, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, + 0x75, 0x6d, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x12, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x42, 0x74, 0x0a, 0x28, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5a, + 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, + 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_resource_definitions_block_block_proto_rawDescOnce sync.Once + file_resource_definitions_block_block_proto_rawDescData = file_resource_definitions_block_block_proto_rawDesc +) + +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(file_resource_definitions_block_block_proto_rawDescData) + }) + return file_resource_definitions_block_block_proto_rawDescData +} + +var file_resource_definitions_block_block_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +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 + (*MountSpec)(nil), // 10: talos.resource.definitions.block.MountSpec + (*PartitionSpec)(nil), // 11: talos.resource.definitions.block.PartitionSpec + (*ProvisioningSpec)(nil), // 12: talos.resource.definitions.block.ProvisioningSpec + (*SystemDiskSpec)(nil), // 13: talos.resource.definitions.block.SystemDiskSpec + (*UserDiskConfigStatusSpec)(nil), // 14: talos.resource.definitions.block.UserDiskConfigStatusSpec + (*VolumeConfigSpec)(nil), // 15: talos.resource.definitions.block.VolumeConfigSpec + (*VolumeStatusSpec)(nil), // 16: talos.resource.definitions.block.VolumeStatusSpec + (*v1alpha1.CheckedExpr)(nil), // 17: google.api.expr.v1alpha1.CheckedExpr + (enums.BlockEncryptionKeyType)(0), // 18: talos.resource.definitions.enums.BlockEncryptionKeyType + (enums.BlockEncryptionProviderType)(0), // 19: talos.resource.definitions.enums.BlockEncryptionProviderType + (enums.BlockFilesystemType)(0), // 20: talos.resource.definitions.enums.BlockFilesystemType + (enums.BlockVolumeType)(0), // 21: talos.resource.definitions.enums.BlockVolumeType + (enums.BlockVolumePhase)(0), // 22: talos.resource.definitions.enums.BlockVolumePhase +} +var file_resource_definitions_block_block_proto_depIdxs = []int32{ + 17, // 0: talos.resource.definitions.block.DiskSelector.match:type_name -> google.api.expr.v1alpha1.CheckedExpr + 18, // 1: talos.resource.definitions.block.EncryptionKey.type:type_name -> talos.resource.definitions.enums.BlockEncryptionKeyType + 19, // 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 + 20, // 4: talos.resource.definitions.block.FilesystemSpec.type:type_name -> talos.resource.definitions.enums.BlockFilesystemType + 17, // 5: talos.resource.definitions.block.LocatorSpec.match:type_name -> google.api.expr.v1alpha1.CheckedExpr + 4, // 6: talos.resource.definitions.block.ProvisioningSpec.disk_selector:type_name -> talos.resource.definitions.block.DiskSelector + 11, // 7: talos.resource.definitions.block.ProvisioningSpec.partition_spec:type_name -> talos.resource.definitions.block.PartitionSpec + 8, // 8: talos.resource.definitions.block.ProvisioningSpec.filesystem_spec:type_name -> talos.resource.definitions.block.FilesystemSpec + 21, // 9: talos.resource.definitions.block.VolumeConfigSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType + 12, // 10: talos.resource.definitions.block.VolumeConfigSpec.provisioning:type_name -> talos.resource.definitions.block.ProvisioningSpec + 9, // 11: talos.resource.definitions.block.VolumeConfigSpec.locator:type_name -> talos.resource.definitions.block.LocatorSpec + 10, // 12: talos.resource.definitions.block.VolumeConfigSpec.mount:type_name -> talos.resource.definitions.block.MountSpec + 7, // 13: talos.resource.definitions.block.VolumeConfigSpec.encryption:type_name -> talos.resource.definitions.block.EncryptionSpec + 22, // 14: talos.resource.definitions.block.VolumeStatusSpec.phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase + 22, // 15: talos.resource.definitions.block.VolumeStatusSpec.pre_fail_phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase + 20, // 16: talos.resource.definitions.block.VolumeStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType + 19, // 17: talos.resource.definitions.block.VolumeStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] 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 + } + if !protoimpl.UnsafeEnabled { + file_resource_definitions_block_block_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*DeviceSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } } file_resource_definitions_block_block_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*DiscoveredVolumeSpec); i { @@ -634,7 +1768,7 @@ func file_resource_definitions_block_block_proto_init() { } } file_resource_definitions_block_block_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*DiskSpec); i { + switch v := v.(*DiscoveryRefreshRequestSpec); i { case 0: return &v.state case 1: @@ -646,6 +1780,126 @@ func file_resource_definitions_block_block_proto_init() { } } file_resource_definitions_block_block_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*DiscoveryRefreshStatusSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*DiskSelector); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*DiskSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*EncryptionKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*EncryptionSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*FilesystemSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*LocatorSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*MountSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*PartitionSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*ProvisioningSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[13].Exporter = func(v any, i int) any { switch v := v.(*SystemDiskSpec); i { case 0: return &v.state @@ -657,6 +1911,42 @@ func file_resource_definitions_block_block_proto_init() { return nil } } + file_resource_definitions_block_block_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*UserDiskConfigStatusSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*VolumeConfigSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_resource_definitions_block_block_proto_msgTypes[16].Exporter = func(v any, i int) any { + switch v := v.(*VolumeStatusSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -664,7 +1954,7 @@ func file_resource_definitions_block_block_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_resource_definitions_block_block_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 17, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go b/pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go index 2c8d9f31944..508306d1f4b 100644 --- a/pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go +++ b/pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go @@ -9,7 +9,11 @@ import ( 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 ( @@ -130,6 +134,24 @@ func (m *DiscoveredVolumeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + 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) @@ -233,6 +255,137 @@ func (m *DiscoveredVolumeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) 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 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 @@ -263,6 +416,13 @@ func (m *DiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { 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] = 0x72 + } if m.Cdrom { i-- if m.Cdrom { @@ -360,7 +520,7 @@ func (m *DiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *SystemDiskSpec) MarshalVT() (dAtA []byte, err error) { +func (m *EncryptionKey) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -373,12 +533,12 @@ func (m *SystemDiskSpec) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *SystemDiskSpec) MarshalToVT(dAtA []byte) (int, error) { +func (m *EncryptionKey) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *SystemDiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *EncryptionKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -390,192 +550,2800 @@ func (m *SystemDiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.DiskId) > 0 { - i -= len(m.DiskId) - copy(dAtA[i:], m.DiskId) - i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DiskId))) + if m.TpmCheckSecurebootStatusOnEnroll { i-- - dAtA[i] = 0xa + 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 *DeviceSpec) SizeVT() (n int) { +func (m *EncryptionSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { - return 0 + return nil, nil + } + size := 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 - l = len(m.Type) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - if m.Major != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Major)) + 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.Minor != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Minor)) + if m.BlockSize != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BlockSize)) + i-- + dAtA[i] = 0x28 } - l = len(m.PartitionName) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + if m.KeySize != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.KeySize)) + i-- + dAtA[i] = 0x20 } - if m.PartitionNumber != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.PartitionNumber)) + 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 m.Generation != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Generation)) + 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 + } } - l = len(m.DevicePath) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + if m.Provider != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Provider)) + i-- + dAtA[i] = 0x8 } - l = len(m.Parent) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *FilesystemSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - n += len(m.unknownFields) - return n + size := 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) SizeVT() (n int) { +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 + return 0, nil } + i := len(dAtA) + _ = i var l int _ = l - if m.Size != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - if m.SectorSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.SectorSize)) + 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.IoSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.IoSize)) + if m.Type != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 } - l = len(m.Name) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *LocatorSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - l = len(m.Uuid) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err } - l = len(m.Label) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - if m.BlockSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.BlockSize)) + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - if m.FilesystemBlockSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.FilesystemBlockSize)) + 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 } - if m.ProbedSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.ProbedSize)) + return len(dAtA) - i, nil +} + +func (m *MountSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - l = len(m.PartitionUuid) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err } - l = len(m.PartitionType) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - l = len(m.PartitionLabel) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - if m.PartitionIndex != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.PartitionIndex)) + 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 } - l = len(m.Type) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *PartitionSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - l = len(m.DevicePath) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err } - l = len(m.Parent) - if l > 0 { - n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - n += len(m.unknownFields) - return n + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + 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 *DiskSpec) SizeVT() (n int) { +func (m *ProvisioningSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { - return 0 + return nil, nil + } + size := 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.Size != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - if m.IoSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.IoSize)) + 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.SectorSize != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.SectorSize)) + if m.Wave != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Wave)) + i-- + dAtA[i] = 0x18 } - if m.Readonly { - n += 2 + 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 } - l = len(m.Model) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - l = len(m.Serial) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *SystemDiskSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - l = len(m.Modalias) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err } - l = len(m.Wwid) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - l = len(m.BusPath) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) } - l = len(m.SubSystem) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - l = len(m.Transport) - if l > 0 { - n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + 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 } - if m.Rotational { - n += 2 + return len(dAtA) - i, nil +} + +func (m *UserDiskConfigStatusSpec) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil } - if m.Cdrom { - n += 2 + size := 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.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.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 *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.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 *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)) + } + 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)) + } + 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)) + } + 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)) + } + 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 + } + 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)) + } + 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)) + } + 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)) + } + 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 *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 *UserDiskConfigStatusSpec) 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 *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)) + } + 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)) + } + 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 + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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 + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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 + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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 + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } } - 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)) + if iNdEx > l { + return io.ErrUnexpectedEOF } - n += len(m.unknownFields) - return n + return nil } - -func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { +func (m *EncryptionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -598,15 +3366,68 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeviceSpec: wiretype end group for non-group") + return fmt.Errorf("proto: EncryptionSpec: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeviceSpec: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Type", wireType) + 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 { @@ -634,13 +3455,13 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Type = string(dAtA[iNdEx:postIndex]) + m.Cipher = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 4: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KeySize", wireType) } - m.Major = 0 + m.KeySize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -650,16 +3471,118 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Major |= int64(b&0x7F) << shift + m.KeySize |= uint64(b&0x7F) << shift if b < 0x80 { break } } - case 3: + 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 Minor", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } - m.Minor = 0 + m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -669,14 +3592,14 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Minor |= int64(b&0x7F) << shift + m.Type |= enums.BlockFilesystemType(b&0x7F) << shift if b < 0x80 { break } } - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PartitionName", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -704,51 +3627,64 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.PartitionName = string(dAtA[iNdEx:postIndex]) + m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PartitionNumber", wireType) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err } - 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 - } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength } - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType) + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF } - 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 - } + 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 } - case 7: + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 DevicePath", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Match", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -758,27 +3694,90 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return protohelpers.ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.DevicePath = string(dAtA[iNdEx:postIndex]) + 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 8: + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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 Parent", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TargetPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -806,7 +3805,7 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Parent = string(dAtA[iNdEx:postIndex]) + m.TargetPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -830,7 +3829,7 @@ func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { +func (m *PartitionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -853,17 +3852,17 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DiscoveredVolumeSpec: wiretype end group for non-group") + return fmt.Errorf("proto: PartitionSpec: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DiscoveredVolumeSpec: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Size", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MinSize", wireType) } - m.Size = 0 + m.MinSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -873,16 +3872,16 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size |= uint64(b&0x7F) << shift + m.MinSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field SectorSize", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxSize", wireType) } - m.SectorSize = 0 + m.MaxSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -892,16 +3891,16 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.SectorSize |= uint64(b&0x7F) << shift + m.MaxSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IoSize", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Grow", wireType) } - m.IoSize = 0 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -911,14 +3910,15 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.IoSize |= uint64(b&0x7F) << shift + 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 Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -946,11 +3946,11 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TypeUuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -978,13 +3978,100 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Uuid = string(dAtA[iNdEx:postIndex]) + m.TypeUuid = 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 *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 6: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PartitionSpec", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -994,29 +4081,33 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return protohelpers.ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.Label = string(dAtA[iNdEx:postIndex]) + if m.PartitionSpec == nil { + m.PartitionSpec = &PartitionSpec{} + } + if err := m.PartitionSpec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 7: + case 3: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field BlockSize", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Wave", wireType) } - m.BlockSize = 0 + m.Wave = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1026,16 +4117,16 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.BlockSize |= uint32(b&0x7F) << shift + m.Wave |= int64(b&0x7F) << shift if b < 0x80 { break } } - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field FilesystemBlockSize", wireType) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FilesystemSpec", wireType) } - m.FilesystemBlockSize = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1045,16 +4136,84 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.FilesystemBlockSize |= uint32(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 9: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ProbedSize", wireType) + if msglen < 0 { + return protohelpers.ErrInvalidLength } - m.ProbedSize = 0 + 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 *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 @@ -1064,14 +4223,27 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ProbedSize |= uint64(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - case 10: + intStringLen := 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 PartitionUuid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DevPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1099,13 +4271,64 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.PartitionUuid = string(dAtA[iNdEx:postIndex]) + m.DevPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 11: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PartitionType", wireType) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err } - var stringLen uint64 + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + 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 @@ -1115,27 +4338,66 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return protohelpers.ErrInvalidLength + m.Ready = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err } - postIndex := iNdEx + intStringLen - if postIndex < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } - if postIndex > l { + if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.PartitionType = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 12: + 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 PartitionLabel", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ParentId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1163,13 +4425,13 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.PartitionLabel = string(dAtA[iNdEx:postIndex]) + m.ParentId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 13: + case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PartitionIndex", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } - m.PartitionIndex = 0 + m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1179,16 +4441,52 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.PartitionIndex |= uint64(b&0x7F) << shift + 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 } } - case 14: + 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 Type", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Locator", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1198,29 +4496,33 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return protohelpers.ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.Type = string(dAtA[iNdEx:postIndex]) + if m.Locator == nil { + m.Locator = &LocatorSpec{} + } + if err := m.Locator.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 15: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DevicePath", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Mount", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1230,29 +4532,33 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return protohelpers.ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.DevicePath = string(dAtA[iNdEx:postIndex]) + if m.Mount == nil { + m.Mount = &MountSpec{} + } + if err := m.Mount.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 16: + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Parent", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Encryption", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1262,23 +4568,27 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return protohelpers.ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.Parent = string(dAtA[iNdEx:postIndex]) + if m.Encryption == nil { + m.Encryption = &EncryptionSpec{} + } + if err := m.Encryption.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -1302,7 +4612,7 @@ func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { +func (m *VolumeStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1325,17 +4635,17 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DiskSpec: wiretype end group for non-group") + return fmt.Errorf("proto: VolumeStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DiskSpec: illegal tag %d (wire type %d)", fieldNum, wire) + 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 Size", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) } - m.Size = 0 + m.Phase = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1345,72 +4655,14 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size |= uint64(b&0x7F) << shift + m.Phase |= enums.BlockVolumePhase(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) + return fmt.Errorf("proto: wrong wireType = %d for field Location", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1438,11 +4690,11 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Model = string(dAtA[iNdEx:postIndex]) + m.Location = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Serial", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ErrorMessage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1470,11 +4722,11 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Serial = string(dAtA[iNdEx:postIndex]) + m.ErrorMessage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Modalias", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1502,11 +4754,11 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Modalias = string(dAtA[iNdEx:postIndex]) + m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 8: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Wwid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PartitionUuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1534,13 +4786,13 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Wwid = string(dAtA[iNdEx:postIndex]) + m.PartitionUuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BusPath", wireType) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PreFailPhase", wireType) } - var stringLen uint64 + m.PreFailPhase = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1550,27 +4802,14 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + m.PreFailPhase |= enums.BlockVolumePhase(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := 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: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SubSystem", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ParentLocation", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1598,13 +4837,13 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.SubSystem = string(dAtA[iNdEx:postIndex]) + m.ParentLocation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 11: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Transport", wireType) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PartitionIndex", wireType) } - var stringLen uint64 + m.PartitionIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1614,29 +4853,16 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + m.PartitionIndex |= int64(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := 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: + case 9: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Rotational", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } - var v int + m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1646,17 +4872,16 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.Rotational = bool(v != 0) - case 13: + case 10: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Cdrom", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Filesystem", wireType) } - var v int + m.Filesystem = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1666,66 +4891,14 @@ func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + m.Filesystem |= enums.BlockFilesystemType(b&0x7F) << shift if b < 0x80 { break } } - m.Cdrom = 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 *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: + case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DiskId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MountLocation", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1753,8 +4926,27 @@ func (m *SystemDiskSpec) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.DiskId = string(dAtA[iNdEx:postIndex]) + 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 + } + } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/pkg/machinery/api/resource/definitions/enums/enums.pb.go b/pkg/machinery/api/resource/definitions/enums/enums.pb.go index 627a30f69ef..c22b7d5d643 100644 --- a/pkg/machinery/api/resource/definitions/enums/enums.pb.go +++ b/pkg/machinery/api/resource/definitions/enums/enums.pb.go @@ -1831,6 +1831,265 @@ func (NethelpersVLANProtocol) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{26} } +// 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[27].Descriptor() +} + +func (BlockEncryptionKeyType) Type() protoreflect.EnumType { + return &file_resource_definitions_enums_enums_proto_enumTypes[27] +} + +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{27} +} + +// 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[28].Descriptor() +} + +func (BlockEncryptionProviderType) Type() protoreflect.EnumType { + return &file_resource_definitions_enums_enums_proto_enumTypes[28] +} + +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{28} +} + +// BlockFilesystemType describes filesystem type. +type BlockFilesystemType int32 + +const ( + BlockFilesystemType_FILESYSTEM_TYPE_NONE BlockFilesystemType = 0 + BlockFilesystemType_FILESYSTEM_TYPE_XFS BlockFilesystemType = 1 +) + +// Enum value maps for BlockFilesystemType. +var ( + BlockFilesystemType_name = map[int32]string{ + 0: "FILESYSTEM_TYPE_NONE", + 1: "FILESYSTEM_TYPE_XFS", + } + BlockFilesystemType_value = map[string]int32{ + "FILESYSTEM_TYPE_NONE": 0, + "FILESYSTEM_TYPE_XFS": 1, + } +) + +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[29].Descriptor() +} + +func (BlockFilesystemType) Type() protoreflect.EnumType { + return &file_resource_definitions_enums_enums_proto_enumTypes[29] +} + +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{29} +} + +// 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 +) + +// 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", + } + 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, + } +) + +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[30].Descriptor() +} + +func (BlockVolumePhase) Type() protoreflect.EnumType { + return &file_resource_definitions_enums_enums_proto_enumTypes[30] +} + +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{30} +} + +// 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 +) + +// Enum value maps for BlockVolumeType. +var ( + BlockVolumeType_name = map[int32]string{ + 0: "VOLUME_TYPE_PARTITION", + 1: "VOLUME_TYPE_DISK", + 2: "VOLUME_TYPE_TMPFS", + } + BlockVolumeType_value = map[string]int32{ + "VOLUME_TYPE_PARTITION": 0, + "VOLUME_TYPE_DISK": 1, + "VOLUME_TYPE_TMPFS": 2, + } +) + +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[31].Descriptor() +} + +func (BlockVolumeType) Type() protoreflect.EnumType { + return &file_resource_definitions_enums_enums_proto_enumTypes[31] +} + +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{31} +} + // KubespanPeerState is KubeSpan peer current state. type KubespanPeerState int32 @@ -1865,11 +2124,11 @@ func (x KubespanPeerState) String() string { } func (KubespanPeerState) Descriptor() protoreflect.EnumDescriptor { - return file_resource_definitions_enums_enums_proto_enumTypes[27].Descriptor() + return file_resource_definitions_enums_enums_proto_enumTypes[32].Descriptor() } func (KubespanPeerState) Type() protoreflect.EnumType { - return &file_resource_definitions_enums_enums_proto_enumTypes[27] + return &file_resource_definitions_enums_enums_proto_enumTypes[32] } func (x KubespanPeerState) Number() protoreflect.EnumNumber { @@ -1878,7 +2137,7 @@ func (x KubespanPeerState) Number() protoreflect.EnumNumber { // Deprecated: Use KubespanPeerState.Descriptor instead. func (KubespanPeerState) EnumDescriptor() ([]byte, []int) { - return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{27} + return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{32} } // NetworkConfigLayer describes network configuration layers, with lowest priority first. @@ -1921,11 +2180,11 @@ func (x NetworkConfigLayer) String() string { } func (NetworkConfigLayer) Descriptor() protoreflect.EnumDescriptor { - return file_resource_definitions_enums_enums_proto_enumTypes[28].Descriptor() + return file_resource_definitions_enums_enums_proto_enumTypes[33].Descriptor() } func (NetworkConfigLayer) Type() protoreflect.EnumType { - return &file_resource_definitions_enums_enums_proto_enumTypes[28] + return &file_resource_definitions_enums_enums_proto_enumTypes[33] } func (x NetworkConfigLayer) Number() protoreflect.EnumNumber { @@ -1934,7 +2193,7 @@ func (x NetworkConfigLayer) Number() protoreflect.EnumNumber { // Deprecated: Use NetworkConfigLayer.Descriptor instead. func (NetworkConfigLayer) EnumDescriptor() ([]byte, []int) { - return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{28} + return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{33} } // NetworkOperator enumerates Talos network operators. @@ -1971,11 +2230,11 @@ func (x NetworkOperator) String() string { } func (NetworkOperator) Descriptor() protoreflect.EnumDescriptor { - return file_resource_definitions_enums_enums_proto_enumTypes[29].Descriptor() + return file_resource_definitions_enums_enums_proto_enumTypes[34].Descriptor() } func (NetworkOperator) Type() protoreflect.EnumType { - return &file_resource_definitions_enums_enums_proto_enumTypes[29] + return &file_resource_definitions_enums_enums_proto_enumTypes[34] } func (x NetworkOperator) Number() protoreflect.EnumNumber { @@ -1984,7 +2243,7 @@ func (x NetworkOperator) Number() protoreflect.EnumNumber { // Deprecated: Use NetworkOperator.Descriptor instead. func (NetworkOperator) EnumDescriptor() ([]byte, []int) { - return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{29} + return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{34} } // RuntimeMachineStage describes the stage of the machine boot/run process. @@ -2039,11 +2298,11 @@ func (x RuntimeMachineStage) String() string { } func (RuntimeMachineStage) Descriptor() protoreflect.EnumDescriptor { - return file_resource_definitions_enums_enums_proto_enumTypes[30].Descriptor() + return file_resource_definitions_enums_enums_proto_enumTypes[35].Descriptor() } func (RuntimeMachineStage) Type() protoreflect.EnumType { - return &file_resource_definitions_enums_enums_proto_enumTypes[30] + return &file_resource_definitions_enums_enums_proto_enumTypes[35] } func (x RuntimeMachineStage) Number() protoreflect.EnumNumber { @@ -2052,7 +2311,7 @@ func (x RuntimeMachineStage) Number() protoreflect.EnumNumber { // Deprecated: Use RuntimeMachineStage.Descriptor instead. func (RuntimeMachineStage) EnumDescriptor() ([]byte, []int) { - return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{30} + return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{35} } var File_resource_definitions_enums_enums_proto protoreflect.FileDescriptor @@ -2419,52 +2678,89 @@ var file_resource_definitions_enums_enums_proto_rawDesc = []byte{ 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x13, 0x56, 0x4c, 0x41, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x38, 0x30, 0x32, 0x31, 0x5f, 0x51, 0x10, 0x80, 0x82, 0x02, 0x12, 0x1a, 0x0a, 0x14, 0x56, 0x4c, 0x41, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, - 0x4c, 0x38, 0x30, 0x32, 0x31, 0x5f, 0x41, 0x44, 0x10, 0xa8, 0x91, 0x02, 0x2a, 0x53, 0x0a, 0x11, - 0x4b, 0x75, 0x62, 0x65, 0x73, 0x70, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x45, 0x45, - 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, - 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, - 0x02, 0x2a, 0x88, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x4e, 0x46, - 0x49, 0x47, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, - 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x43, 0x4d, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, - 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x50, 0x4c, 0x41, 0x54, 0x46, - 0x4f, 0x52, 0x4d, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, - 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, - 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x43, 0x4f, 0x4e, - 0x46, 0x49, 0x47, 0x55, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x2a, 0x4b, 0x0a, 0x0f, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, - 0x12, 0x0a, 0x0e, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x44, 0x48, 0x43, 0x50, - 0x34, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, - 0x44, 0x48, 0x43, 0x50, 0x36, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x50, 0x45, 0x52, 0x41, - 0x54, 0x4f, 0x52, 0x5f, 0x56, 0x49, 0x50, 0x10, 0x02, 0x2a, 0x9b, 0x02, 0x0a, 0x13, 0x52, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, - 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, - 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x42, 0x4f, - 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x41, 0x43, 0x48, 0x49, - 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, - 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, - 0x43, 0x45, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, - 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, - 0x1b, 0x0a, 0x17, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, - 0x5f, 0x52, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, - 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x48, - 0x55, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x06, 0x12, 0x1b, 0x0a, - 0x17, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, - 0x45, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, - 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x50, 0x47, 0x52, - 0x41, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x08, 0x42, 0x74, 0x0a, 0x28, 0x64, 0x65, 0x76, 0x2e, 0x74, - 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, - 0x75, 0x6d, 0x73, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4c, 0x38, 0x30, 0x32, 0x31, 0x5f, 0x41, 0x44, 0x10, 0xa8, 0x91, 0x02, 0x2a, 0x7f, 0x0a, 0x16, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x10, + 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x4b, 0x45, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, + 0x12, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x5f, + 0x4b, 0x4d, 0x53, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x54, 0x50, 0x4d, 0x10, 0x03, 0x2a, 0x5a, 0x0a, + 0x1b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, + 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, + 0x44, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x4e, + 0x43, 0x52, 0x59, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, + 0x52, 0x5f, 0x4c, 0x55, 0x4b, 0x53, 0x32, 0x10, 0x01, 0x2a, 0x48, 0x0a, 0x13, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x18, 0x0a, 0x14, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x49, + 0x4c, 0x45, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x58, 0x46, + 0x53, 0x10, 0x01, 0x2a, 0xca, 0x01, 0x0a, 0x10, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4f, 0x4c, 0x55, + 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, + 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, + 0x53, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x56, + 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, + 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, + 0x1c, 0x0a, 0x18, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, + 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x04, 0x12, 0x19, 0x0a, + 0x15, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x52, + 0x45, 0x50, 0x41, 0x52, 0x45, 0x44, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x4f, 0x4c, 0x55, + 0x4d, 0x45, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x06, + 0x2a, 0x59, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x14, + 0x0a, 0x10, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, + 0x53, 0x4b, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x4f, 0x4c, 0x55, 0x4d, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x54, 0x4d, 0x50, 0x46, 0x53, 0x10, 0x02, 0x2a, 0x53, 0x0a, 0x11, 0x4b, + 0x75, 0x62, 0x65, 0x73, 0x70, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x16, 0x0a, 0x12, 0x50, 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x45, 0x45, 0x52, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x50, + 0x45, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x02, + 0x2a, 0x88, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x43, 0x4d, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, + 0x13, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x50, 0x4c, 0x41, 0x54, 0x46, 0x4f, + 0x52, 0x4d, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4f, + 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x4f, 0x4e, + 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x47, 0x55, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x2a, 0x4b, 0x0a, 0x0f, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x12, + 0x0a, 0x0e, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x44, 0x48, 0x43, 0x50, 0x34, + 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x44, + 0x48, 0x43, 0x50, 0x36, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x56, 0x49, 0x50, 0x10, 0x02, 0x2a, 0x9b, 0x02, 0x0a, 0x13, 0x52, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, + 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4d, + 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x42, 0x4f, 0x4f, + 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, + 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x49, + 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, + 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, 0x43, + 0x45, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x1b, + 0x0a, 0x17, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, + 0x52, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, + 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x48, 0x55, + 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x06, 0x12, 0x1b, 0x0a, 0x17, + 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, + 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x41, 0x43, + 0x48, 0x49, 0x4e, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x50, 0x47, 0x52, 0x41, + 0x44, 0x49, 0x4e, 0x47, 0x10, 0x08, 0x42, 0x74, 0x0a, 0x28, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x61, + 0x6c, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x65, 0x6e, 0x75, + 0x6d, 0x73, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, + 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2479,7 +2775,7 @@ func file_resource_definitions_enums_enums_proto_rawDescGZIP() []byte { return file_resource_definitions_enums_enums_proto_rawDescData } -var file_resource_definitions_enums_enums_proto_enumTypes = make([]protoimpl.EnumInfo, 31) +var file_resource_definitions_enums_enums_proto_enumTypes = make([]protoimpl.EnumInfo, 36) var file_resource_definitions_enums_enums_proto_goTypes = []any{ (MachineType)(0), // 0: talos.resource.definitions.enums.MachineType (NethelpersAddressFlag)(0), // 1: talos.resource.definitions.enums.NethelpersAddressFlag @@ -2508,10 +2804,15 @@ var file_resource_definitions_enums_enums_proto_goTypes = []any{ (NethelpersRoutingTable)(0), // 24: talos.resource.definitions.enums.NethelpersRoutingTable (NethelpersScope)(0), // 25: talos.resource.definitions.enums.NethelpersScope (NethelpersVLANProtocol)(0), // 26: talos.resource.definitions.enums.NethelpersVLANProtocol - (KubespanPeerState)(0), // 27: talos.resource.definitions.enums.KubespanPeerState - (NetworkConfigLayer)(0), // 28: talos.resource.definitions.enums.NetworkConfigLayer - (NetworkOperator)(0), // 29: talos.resource.definitions.enums.NetworkOperator - (RuntimeMachineStage)(0), // 30: talos.resource.definitions.enums.RuntimeMachineStage + (BlockEncryptionKeyType)(0), // 27: talos.resource.definitions.enums.BlockEncryptionKeyType + (BlockEncryptionProviderType)(0), // 28: talos.resource.definitions.enums.BlockEncryptionProviderType + (BlockFilesystemType)(0), // 29: talos.resource.definitions.enums.BlockFilesystemType + (BlockVolumePhase)(0), // 30: talos.resource.definitions.enums.BlockVolumePhase + (BlockVolumeType)(0), // 31: talos.resource.definitions.enums.BlockVolumeType + (KubespanPeerState)(0), // 32: talos.resource.definitions.enums.KubespanPeerState + (NetworkConfigLayer)(0), // 33: talos.resource.definitions.enums.NetworkConfigLayer + (NetworkOperator)(0), // 34: talos.resource.definitions.enums.NetworkOperator + (RuntimeMachineStage)(0), // 35: talos.resource.definitions.enums.RuntimeMachineStage } var file_resource_definitions_enums_enums_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -2531,7 +2832,7 @@ func file_resource_definitions_enums_enums_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_resource_definitions_enums_enums_proto_rawDesc, - NumEnums: 31, + NumEnums: 36, NumMessages: 0, NumExtensions: 0, NumServices: 0, diff --git a/pkg/machinery/cel/cel.go b/pkg/machinery/cel/cel.go new file mode 100644 index 00000000000..eeba5cb906f --- /dev/null +++ b/pkg/machinery/cel/cel.go @@ -0,0 +1,159 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/siderolabs/go-pointer" + "github.com/siderolabs/protoenc" + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + "gopkg.in/yaml.v3" + + "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, issues := env.Parse(expression) + if issues != nil && issues.Err() != nil { + return Expression{}, issues.Err() + } + + ast, issues = env.Check(ast) + if issues != nil && issues.Err() != nil { + return Expression{}, issues.Err() + } + + if outputType := ast.OutputType(); !outputType.IsExactType(types.BoolType) { + return Expression{}, fmt.Errorf("expression output type is %s, expected bool", outputType) + } + + return Expression{ast: ast}, nil +} + +// EvalBool evaluates the expression in the given environment. +func (expr Expression) EvalBool(env *cel.Env, values map[string]any) (bool, error) { + 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 +} + +// 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 = pointer.To(string(data)) + + return nil +} + +// 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 + }, + ) +} diff --git a/pkg/machinery/cel/cel_test.go b/pkg/machinery/cel/cel_test.go new file mode 100644 index 00000000000..e79ac1b128a --- /dev/null +++ b/pkg/machinery/cel/cel_test.go @@ -0,0 +1,66 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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/require" + "gopkg.in/yaml.v3" + + "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)) + }) + } +} diff --git a/pkg/machinery/cel/celenv/celenv.go b/pkg/machinery/cel/celenv/celenv.go new file mode 100644 index 00000000000..aec86ce4587 --- /dev/null +++ b/pkg/machinery/cel/celenv/celenv.go @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 ( + "sync" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + + "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" +) + +// DiskLocator is a disk locator CEL environment. +var DiskLocator = sync.OnceValue(func() *cel.Env { + var diskSpec block.DiskSpec + + env, err := cel.NewEnv( + cel.Types(&diskSpec), + cel.Variable("disk", cel.ObjectType(string(diskSpec.ProtoReflect().Descriptor().FullName()))), + cel.Variable("system_disk", types.BoolType), + ) + 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 + + env, err := cel.NewEnv( + cel.Types(&volumeSpec), + cel.Variable("volume", cel.ObjectType(string(volumeSpec.ProtoReflect().Descriptor().FullName()))), + ) + if err != nil { + panic(err) + } + + return env +}) diff --git a/pkg/machinery/cel/celenv/celenv_test.go b/pkg/machinery/cel/celenv/celenv_test.go new file mode 100644 index 00000000000..e09e0b88b22 --- /dev/null +++ b/pkg/machinery/cel/celenv/celenv_test.go @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" +) + +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 && !disk.rotational", + }, + } { + 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'", + }, + } { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + _, err := cel.ParseBooleanExpression(test.expression, env) + require.NoError(t, err) + }) + } +} diff --git a/pkg/machinery/compatibility/talos_version.go b/pkg/machinery/compatibility/talos_version.go index cfa80abab0d..a783c5dd953 100644 --- a/pkg/machinery/compatibility/talos_version.go +++ b/pkg/machinery/compatibility/talos_version.go @@ -52,6 +52,17 @@ func (v *TalosVersion) DisablePredictableNetworkInterfaces() bool { 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 diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index 1b2206281bb..45b4cd2369d 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -17,8 +17,8 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice/encryption" "github.com/siderolabs/go-blockdevice/blockdevice/util/disk" + "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" diff --git a/pkg/machinery/go.mod b/pkg/machinery/go.mod index d804058ed2e..6b7debf99f6 100644 --- a/pkg/machinery/go.mod +++ b/pkg/machinery/go.mod @@ -15,6 +15,7 @@ require ( github.com/evanphx/json-patch v5.9.0+incompatible github.com/fatih/color v1.17.0 github.com/ghodss/yaml v1.0.0 + github.com/google/cel-go v0.20.1 github.com/hashicorp/go-multierror v1.1.1 github.com/hexops/gotextdiff v1.0.3 github.com/jsimonetti/rtnetlink/v2 v2.0.2 @@ -26,10 +27,12 @@ require ( github.com/siderolabs/gen v0.5.0 github.com/siderolabs/go-api-signature v0.3.4 github.com/siderolabs/go-blockdevice v0.4.7 + github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb github.com/siderolabs/go-pointer v1.0.0 github.com/siderolabs/net v0.4.0 github.com/siderolabs/protoenc v0.2.1 github.com/stretchr/testify v1.9.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 @@ -41,6 +44,7 @@ require ( github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/ProtonMail/gopenpgp/v2 v2.7.5 // indirect github.com/adrg/xdg v0.4.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/containernetworking/cni v1.2.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -58,6 +62,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.26.0 // indirect @@ -67,6 +72,5 @@ require ( golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/pkg/machinery/go.sum b/pkg/machinery/go.sum index ba1d08368e0..5935cdbac74 100644 --- a/pkg/machinery/go.sum +++ b/pkg/machinery/go.sum @@ -7,6 +7,8 @@ github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7Nj github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/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/v6 v6.24.0 h1:74yq7RRz/noddscZHRS2T84oHZisW9muwbb8sRnU52A= @@ -43,6 +45,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-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/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= @@ -105,6 +109,8 @@ github.com/siderolabs/go-api-signature v0.3.4 h1:bl8qiwhKLVpdzmjzWtKHTvWZyb7Oe4d github.com/siderolabs/go-api-signature v0.3.4/go.mod h1:qp7De5ZrF021aPrhm5MyLPuaRhkiX4BADmZweqChw4I= github.com/siderolabs/go-blockdevice v0.4.7 h1:2bk4WpEEflGxjrNwp57ye24Pr+cYgAiAeNMWiQOuWbQ= github.com/siderolabs/go-blockdevice v0.4.7/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA= +github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb h1:zhvZ4cWyaNwJvpMmecvWffM11E6x8ON8O8Dkmb3DO8k= +github.com/siderolabs/go-blockdevice/v2 v2.0.0-20240826151817-08a7802e22eb/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w= github.com/siderolabs/go-pointer v1.0.0 h1:6TshPKep2doDQJAAtHUuHWXbca8ZfyRySjSBT/4GsMU= github.com/siderolabs/go-pointer v1.0.0/go.mod h1:HTRFUNYa3R+k0FFKNv11zgkaCLzEkWVzoYZ433P3kHc= github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg= @@ -113,9 +119,12 @@ 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.1 h1:BqxEmeWQeMpNP3R6WrPqDatX8sM/r4t97OP8mFmg6GA= github.com/siderolabs/protoenc v0.2.1/go.mod h1:StTHxjet1g11GpNAWiATgc8K0HMKiFSEVVFOa/H0otc= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -200,5 +209,6 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn 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.2.2/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= diff --git a/pkg/machinery/imager/quirks/quirks.go b/pkg/machinery/imager/quirks/quirks.go index 1b8354ca57b..cd5d4654ed4 100644 --- a/pkg/machinery/imager/quirks/quirks.go +++ b/pkg/machinery/imager/quirks/quirks.go @@ -98,3 +98,15 @@ func (q Quirks) SupportsMultidoc() bool { return q.v.GTE(minVersionMultidoc) } + +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) +} diff --git a/pkg/machinery/resources/block/block.go b/pkg/machinery/resources/block/block.go index 3ca9b814f62..acb58dbc699 100644 --- a/pkg/machinery/resources/block/block.go +++ b/pkg/machinery/resources/block/block.go @@ -6,12 +6,59 @@ 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/talos/pkg/machinery/resources/v1alpha1" ) -//go:generate deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiskSpec -type SystemDiskSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . +//go:generate deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . + +//go:generate enumer -type=VolumeType,VolumePhase,FilesystemType,EncryptionKeyType,EncryptionProviderType -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" + +// 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 +} diff --git a/pkg/machinery/resources/block/block_test.go b/pkg/machinery/resources/block/block_test.go index f5314a6c68c..e7806f1ce83 100644 --- a/pkg/machinery/resources/block/block_test.go +++ b/pkg/machinery/resources/block/block_test.go @@ -26,9 +26,14 @@ func TestRegisterResource(t *testing.T) { for _, resource := range []meta.ResourceWithRD{ &block.Device{}, + &block.DiscoveryRefreshRequest{}, + &block.DiscoveryRefreshStatus{}, &block.DiscoveredVolume{}, &block.Disk{}, &block.SystemDisk{}, + &block.UserDiskConfigStatus{}, + &block.VolumeConfig{}, + &block.VolumeStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } diff --git a/pkg/machinery/resources/block/deep_copy.generated.go b/pkg/machinery/resources/block/deep_copy.generated.go index 720526e3e23..70e1a5af11e 100644 --- a/pkg/machinery/resources/block/deep_copy.generated.go +++ b/pkg/machinery/resources/block/deep_copy.generated.go @@ -2,7 +2,7 @@ // License, v. 2.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 DiskSpec -type SystemDiskSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. +// Code generated by "deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package block @@ -18,6 +18,18 @@ func (o DiscoveredVolumeSpec) DeepCopy() DiscoveredVolumeSpec { 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 @@ -29,3 +41,35 @@ 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.PerfOptions != nil { + cp.Encryption.PerfOptions = make([]string, len(o.Encryption.PerfOptions)) + copy(cp.Encryption.PerfOptions, o.Encryption.PerfOptions) + } + return cp +} + +// DeepCopy generates a deep copy of VolumeStatusSpec. +func (o VolumeStatusSpec) DeepCopy() VolumeStatusSpec { + var cp VolumeStatusSpec = o + return cp +} diff --git a/pkg/machinery/resources/block/discovered_volume.go b/pkg/machinery/resources/block/discovered_volume.go index 43e6804bcaa..e2f6107f21d 100644 --- a/pkg/machinery/resources/block/discovered_volume.go +++ b/pkg/machinery/resources/block/discovered_volume.go @@ -13,7 +13,7 @@ import ( "github.com/siderolabs/talos/pkg/machinery/proto" ) -// DiscoveredVolumeType is type of BlockDiscoveredVolume resource. +// DiscoveredVolumeType is type of DiscoveredVolume resource. const DiscoveredVolumeType = resource.Type("DiscoveredVolumes.block.talos.dev") // DiscoveredVolume resource holds status of hardware DiscoveredVolumes (overall). @@ -23,31 +23,33 @@ type DiscoveredVolume = typed.Resource[DiscoveredVolumeSpec, DiscoveredVolumeExt // //gotagsrewrite:gen type DiscoveredVolumeSpec struct { - Type string `yaml:"type" protobuf:"14"` - DevicePath string `yaml:"devicePath" protobuf:"15"` - Parent string `yaml:"parent,omitempty" protobuf:"16"` + DevPath string `yaml:"dev_dath" 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"` // Overall size of the probed device (in bytes). Size uint64 `yaml:"size" protobuf:"1"` // Sector size of the device (in bytes). - SectorSize uint `yaml:"sectorSize,omitempty" protobuf:"2"` + SectorSize uint `yaml:"sector_size,omitempty" protobuf:"2"` // Optimal I/O size for the device (in bytes). - IOSize uint `yaml:"ioSize,omitempty" protobuf:"3"` + 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:"blockSize,omitempty" protobuf:"7"` - FilesystemBlockSize uint32 `yaml:"filesystemBlockSize,omitempty" protobuf:"8"` - ProbedSize uint64 `yaml:"probedSize,omitempty" protobuf:"9"` + 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:"partitionUUID,omitempty" protobuf:"10"` - PartitionType string `yaml:"partitionType,omitempty" protobuf:"11"` - PartitionLabel string `yaml:"partitionLabel,omitempty" protobuf:"12"` - PartitionIndex uint `yaml:"partitionIndex,omitempty" protobuf:"13"` + 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"` } // NewDiscoveredVolume initializes a BlockDiscoveredVolume resource. @@ -86,7 +88,7 @@ func (DiscoveredVolumeExtension) ResourceDefinition() meta.ResourceDefinitionSpe }, { Name: "PartitionLabel", - JSONPath: `{.partitionLabel}`, + JSONPath: `{.partition_label}`, }, }, } diff --git a/pkg/machinery/resources/block/discovery_refresh_request.go b/pkg/machinery/resources/block/discovery_refresh_request.go new file mode 100644 index 00000000000..c76ea5c1802 --- /dev/null +++ b/pkg/machinery/resources/block/discovery_refresh_request.go @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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[DiscoveryRefreshRequestSpec](DiscoveryRefreshRequestType, &DiscoveryRefreshRequest{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/block/discovery_refresh_status.go b/pkg/machinery/resources/block/discovery_refresh_status.go new file mode 100644 index 00000000000..3197cfa9434 --- /dev/null +++ b/pkg/machinery/resources/block/discovery_refresh_status.go @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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[DiscoveryRefreshStatusSpec](DiscoveryRefreshStatusType, &DiscoveryRefreshStatus{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/block/disk.go b/pkg/machinery/resources/block/disk.go index 3c393d307f6..985bfd507b7 100644 --- a/pkg/machinery/resources/block/disk.go +++ b/pkg/machinery/resources/block/disk.go @@ -23,9 +23,11 @@ type Disk = typed.Resource[DiskSpec, DiskExtension] // //gotagsrewrite:gen type DiskSpec struct { + DevPath string `yaml:"dev_path" protobuf:"14"` + Size uint64 `yaml:"size" protobuf:"1"` - IOSize uint `yaml:"ioSize" protobuf:"2"` - SectorSize uint `yaml:"sectorSize" protobuf:"3"` + 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"` @@ -34,8 +36,8 @@ type DiskSpec struct { Serial string `yaml:"serial,omitempty" protobuf:"6"` Modalias string `yaml:"modalias,omitempty" protobuf:"7"` WWID string `yaml:"wwid,omitempty" protobuf:"8"` - BusPath string `yaml:"busPath,omitempty" protobuf:"9"` - SubSystem string `yaml:"subSystem,omitempty" protobuf:"10"` + 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"` } diff --git a/pkg/machinery/resources/block/encryptionkeytype.go b/pkg/machinery/resources/block/encryptionkeytype.go new file mode 100644 index 00000000000..ac7538a85b4 --- /dev/null +++ b/pkg/machinery/resources/block/encryptionkeytype.go @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 +) diff --git a/pkg/machinery/resources/block/encryptionprovidertype.go b/pkg/machinery/resources/block/encryptionprovidertype.go new file mode 100644 index 00000000000..e281024ace1 --- /dev/null +++ b/pkg/machinery/resources/block/encryptionprovidertype.go @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 +) diff --git a/pkg/machinery/resources/block/filesystemtype.go b/pkg/machinery/resources/block/filesystemtype.go new file mode 100644 index 00000000000..f1be375135d --- /dev/null +++ b/pkg/machinery/resources/block/filesystemtype.go @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 +) diff --git a/pkg/machinery/resources/block/system_disk.go b/pkg/machinery/resources/block/system_disk.go index cc620ce9e06..fc3c8a84d9f 100644 --- a/pkg/machinery/resources/block/system_disk.go +++ b/pkg/machinery/resources/block/system_disk.go @@ -26,7 +26,8 @@ const SystemDiskID resource.ID = "system-disk" // //gotagsrewrite:gen type SystemDiskSpec struct { - DiskID string `yaml:"diskID" protobuf:"1"` + DiskID string `yaml:"diskID" protobuf:"1"` + DevPath string `yaml:"devPath" protobuf:"2"` } // NewSystemDisk initializes a BlockSystemDisk resource. diff --git a/pkg/machinery/resources/block/user_disk_config_status.go b/pkg/machinery/resources/block/user_disk_config_status.go new file mode 100644 index 00000000000..efecfadfc26 --- /dev/null +++ b/pkg/machinery/resources/block/user_disk_config_status.go @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 status. +// +//gotagsrewrite:gen +type UserDiskConfigStatusSpec struct { + Ready bool `yaml:"ready" protobuf:"1"` +} + +// 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[UserDiskConfigStatusSpec](UserDiskConfigStatusType, &UserDiskConfigStatus{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/block/volume_config.go b/pkg/machinery/resources/block/volume_config.go new file mode 100644 index 00000000000..119269430b6 --- /dev/null +++ b/pkg/machinery/resources/block/volume_config.go @@ -0,0 +1,181 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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/cel" + "github.com/siderolabs/talos/pkg/machinery/proto" +) + +// 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"` +} + +// Wave constants. +const ( + WaveSystemDisk = -1 + WaveUserDisks = 0 +) + +// 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"` +} + +// 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"` + + // 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"` +} + +// LocatorSpec is the spec for volume locator. +// +//gotagsrewrite:gen +type LocatorSpec struct { + Match cel.Expression `yaml:"match,omitempty" protobuf:"1"` +} + +// 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" 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"` + + // Only for Type == "static": + StaticPassphrase []byte `yaml:"staticPassphrase" protobuf:"3"` + + // Only for Type == "kms": + KMSEndpoint string `yaml:"kmsEndpoint,omitempty" protobuf:"4"` + + // Only for Type == "tpm": + TPMCheckSecurebootStatusOnEnroll bool `yaml:"tpmCheckSecurebootStatusOnEnroll" protobuf:"5"` +} + +// MountSpec is the spec for volume mount. +// +//gotagsrewrite:gen +type MountSpec struct { + // Mount path for the volume. + TargetPath string `yaml:"targetPath" protobuf:"1"` +} + +// 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[VolumeConfigSpec](VolumeConfigType, &VolumeConfig{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/block/volume_status.go b/pkg/machinery/resources/block/volume_status.go new file mode 100644 index 00000000000..6faaa5f5faa --- /dev/null +++ b/pkg/machinery/resources/block/volume_status.go @@ -0,0 +1,88 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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" +) + +// 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"` + + // 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"` + + // Filesystem is the filesystem type. + Filesystem FilesystemType `yaml:"filesystem,omitempty" protobuf:"10"` + + // EncryptionProvider is the provider of the encryption. + EncryptionProvider EncryptionProviderType `yaml:"encryptionProvider,omitempty" protobuf:"12"` + + ErrorMessage string `yaml:"errorMessage,omitempty" protobuf:"3"` +} + +// 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: "Phase", + JSONPath: `{.phase}`, + }, + { + Name: "Location", + JSONPath: `{.location}`, + }, + }, + } +} + +func init() { + proto.RegisterDefaultTypes() + + err := protobuf.RegisterDynamic[VolumeStatusSpec](VolumeStatusType, &VolumeStatus{}) + if err != nil { + panic(err) + } +} diff --git a/pkg/machinery/resources/block/volumephase.go b/pkg/machinery/resources/block/volumephase.go new file mode 100644 index 00000000000..cb269408a56 --- /dev/null +++ b/pkg/machinery/resources/block/volumephase.go @@ -0,0 +1,21 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 +) diff --git a/pkg/machinery/resources/block/volumetype.go b/pkg/machinery/resources/block/volumetype.go new file mode 100644 index 00000000000..6e8f665f1f1 --- /dev/null +++ b/pkg/machinery/resources/block/volumetype.go @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. 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 +) diff --git a/pkg/machinery/resources/block/volumetype_enumer.go b/pkg/machinery/resources/block/volumetype_enumer.go new file mode 100644 index 00000000000..d9fb9a8d57c --- /dev/null +++ b/pkg/machinery/resources/block/volumetype_enumer.go @@ -0,0 +1,450 @@ +// Code generated by "enumer -type=VolumeType,VolumePhase,FilesystemType,EncryptionKeyType,EncryptionProviderType -linecomment -text"; DO NOT EDIT. + +package block + +import ( + "fmt" + "strings" +) + +const _VolumeTypeName = "partitiondisktmpfs" + +var _VolumeTypeIndex = [...]uint8{0, 9, 13, 18} + +const _VolumeTypeLowerName = "partitiondisktmpfs" + +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)] +} + +var _VolumeTypeValues = []VolumeType{VolumeTypePartition, VolumeTypeDisk, VolumeTypeTmpfs} + +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, +} + +var _VolumeTypeNames = []string{ + _VolumeTypeName[0:9], + _VolumeTypeName[9:13], + _VolumeTypeName[13:18], +} + +// 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 = "waitingfailedmissinglocatedprovisionedpreparedready" + +var _VolumePhaseIndex = [...]uint8{0, 7, 13, 20, 27, 38, 46, 51} + +const _VolumePhaseLowerName = "waitingfailedmissinglocatedprovisionedpreparedready" + +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)] +} + +var _VolumePhaseValues = []VolumePhase{VolumePhaseWaiting, VolumePhaseFailed, VolumePhaseMissing, VolumePhaseLocated, VolumePhaseProvisioned, VolumePhasePrepared, VolumePhaseReady} + +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, +} + +var _VolumePhaseNames = []string{ + _VolumePhaseName[0:7], + _VolumePhaseName[7:13], + _VolumePhaseName[13:20], + _VolumePhaseName[20:27], + _VolumePhaseName[27:38], + _VolumePhaseName[38:46], + _VolumePhaseName[46:51], +} + +// 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 = "nonexfs" + +var _FilesystemTypeIndex = [...]uint8{0, 4, 7} + +const _FilesystemTypeLowerName = "nonexfs" + +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)] +} + +var _FilesystemTypeValues = []FilesystemType{FilesystemTypeNone, FilesystemTypeXFS} + +var _FilesystemTypeNameToValueMap = map[string]FilesystemType{ + _FilesystemTypeName[0:4]: FilesystemTypeNone, + _FilesystemTypeLowerName[0:4]: FilesystemTypeNone, + _FilesystemTypeName[4:7]: FilesystemTypeXFS, + _FilesystemTypeLowerName[4:7]: FilesystemTypeXFS, +} + +var _FilesystemTypeNames = []string{ + _FilesystemTypeName[0:4], + _FilesystemTypeName[4:7], +} + +// 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 +} diff --git a/pkg/provision/providers/qemu/launch.go b/pkg/provision/providers/qemu/launch.go index 1a1ff44c67a..76e0bc87493 100644 --- a/pkg/provision/providers/qemu/launch.go +++ b/pkg/provision/providers/qemu/launch.go @@ -26,7 +26,7 @@ import ( "github.com/coreos/go-iptables/iptables" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt" + "github.com/siderolabs/go-blockdevice/v2/blkid" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/provision" @@ -275,27 +275,12 @@ func withCNI(ctx context.Context, config *LaunchConfig, f func(config *LaunchCon } func checkPartitions(config *LaunchConfig) (bool, error) { - disk, err := os.Open(config.DiskPaths[0]) + info, err := blkid.ProbePath(config.DiskPaths[0]) if err != nil { - return false, fmt.Errorf("failed to open disk file %w", err) + return false, fmt.Errorf("error probing disk: %w", err) } - defer disk.Close() //nolint:errcheck - - diskTable, err := gpt.Open(disk) - if err != nil { - if errors.Is(err, gpt.ErrPartitionTableDoesNotExist) { - return false, nil - } - - return false, fmt.Errorf("error creating GPT object: %w", err) - } - - if err = diskTable.Read(); err != nil { - return false, err - } - - return len(diskTable.Partitions().Items()) > 0, nil + return info.Name == "gpt" && len(info.Parts) > 0, nil } // launchVM runs qemu with args built based on config. diff --git a/website/content/v1.8/reference/api.md b/website/content/v1.8/reference/api.md index 2662a7a4689..e2607f61b15 100644 --- a/website/content/v1.8/reference/api.md +++ b/website/content/v1.8/reference/api.md @@ -29,8 +29,21 @@ description: Talos gRPC API reference. - [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) + - [MountSpec](#talos.resource.definitions.block.MountSpec) + - [PartitionSpec](#talos.resource.definitions.block.PartitionSpec) + - [ProvisioningSpec](#talos.resource.definitions.block.ProvisioningSpec) - [SystemDiskSpec](#talos.resource.definitions.block.SystemDiskSpec) + - [UserDiskConfigStatusSpec](#talos.resource.definitions.block.UserDiskConfigStatusSpec) + - [VolumeConfigSpec](#talos.resource.definitions.block.VolumeConfigSpec) + - [VolumeStatusSpec](#talos.resource.definitions.block.VolumeStatusSpec) - [resource/definitions/cluster/cluster.proto](#resource/definitions/cluster/cluster.proto) - [AffiliateSpec](#talos.resource.definitions.cluster.AffiliateSpec) @@ -45,6 +58,11 @@ description: Talos gRPC API reference. - [SeccompProfileSpec](#talos.resource.definitions.cri.SeccompProfileSpec) - [resource/definitions/enums/enums.proto](#resource/definitions/enums/enums.proto) + - [BlockEncryptionKeyType](#talos.resource.definitions.enums.BlockEncryptionKeyType) + - [BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) + - [BlockFilesystemType](#talos.resource.definitions.enums.BlockFilesystemType) + - [BlockVolumePhase](#talos.resource.definitions.enums.BlockVolumePhase) + - [BlockVolumeType](#talos.resource.definitions.enums.BlockVolumeType) - [KubespanPeerState](#talos.resource.definitions.enums.KubespanPeerState) - [MachineType](#talos.resource.definitions.enums.MachineType) - [NethelpersADSelect](#talos.resource.definitions.enums.NethelpersADSelect) @@ -814,6 +832,53 @@ DiscoveredVolumeSpec is the spec for DiscoveredVolumes status. | type | [string](#string) | | | | device_path | [string](#string) | | | | parent | [string](#string) | | | +| dev_path | [string](#string) | | | +| parent_dev_path | [string](#string) | | | + + + + + + + + +### 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) | | | @@ -841,6 +906,129 @@ DiskSpec is the spec for Disks status. | transport | [string](#string) | | | | rotational | [bool](#bool) | | | | cdrom | [bool](#bool) | | | +| dev_path | [string](#string) | | | + + + + + + + + +### 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) | | | + + + + + + + + +### 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) | | | + + + + + + + + +### MountSpec +MountSpec is the spec for volume mount. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| target_path | [string](#string) | | | + + + + + + + + +### 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) | | | + + + + + + + + +### 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) | | | @@ -856,6 +1044,68 @@ SystemDiskSpec is the spec for SystemDisks status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | disk_id | [string](#string) | | | +| dev_path | [string](#string) | | | + + + + + + + + +### UserDiskConfigStatusSpec +UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus status. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| ready | [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) | | | + + + + + + + + +### 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) | | | @@ -1058,6 +1308,74 @@ SeccompProfileSpec represents the SeccompProfile. + + +### 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 | | + + + + + +### BlockFilesystemType +BlockFilesystemType describes filesystem type. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| FILESYSTEM_TYPE_NONE | 0 | | +| FILESYSTEM_TYPE_XFS | 1 | | + + + + + +### 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 | | + + + + + +### BlockVolumeType +BlockVolumeType describes volume type. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| VOLUME_TYPE_PARTITION | 0 | | +| VOLUME_TYPE_DISK | 1 | | +| VOLUME_TYPE_TMPFS | 2 | | + + + ### KubespanPeerState diff --git a/website/content/v1.8/reference/cli.md b/website/content/v1.8/reference/cli.md index e8f6b82fb66..412a4b3baca 100644 --- a/website/content/v1.8/reference/cli.md +++ b/website/content/v1.8/reference/cli.md @@ -2903,7 +2903,6 @@ talosctl upgrade [flags] -h, --help help for upgrade -i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.8.0-alpha.1") --insecure upgrade using the insecure (encrypted with no auth) maintenance service - -p, --preserve preserve data -m, --reboot-mode string select the reboot mode during upgrade. Mode "powercycle" bypasses kexec. Valid values are: ["default" "powercycle"]. (default "default") -s, --stage stage the upgrade to perform it after a reboot --timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s)