Skip to content

Commit

Permalink
Ephemeral machine volumes (#432)
Browse files Browse the repository at this point in the history
This change introduces the ability for machines to request ephemeral
volumes. Additionally, the ephemeral handling for machine sub-objects
(i.e. currently `NetworkInterface` and `Volume`) has been improved to
include status fields informing about the binding phase.
  • Loading branch information
adracus authored Jun 2, 2022
1 parent 6a13753 commit f772a16
Show file tree
Hide file tree
Showing 39 changed files with 1,233 additions and 213 deletions.
8 changes: 8 additions & 0 deletions apis/compute/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package compute

import (
"github.com/onmetal/onmetal-api/apis/networking"
"github.com/onmetal/onmetal-api/apis/storage"
)

// EphemeralNetworkInterfaceSource is a definition for an ephemeral (i.e. coupled to the lifetime of the surrounding
Expand All @@ -24,3 +25,10 @@ type EphemeralNetworkInterfaceSource struct {
// NetworkInterfaceTemplate is the template definition of the networking.NetworkInterface.
NetworkInterfaceTemplate *networking.NetworkInterfaceTemplateSpec
}

// EphemeralVolumeSource is a definition for an ephemeral (i.e. coupled to the lifetime of the surrounding object)
// storage.Volume.
type EphemeralVolumeSource struct {
// VolumeTemplate is the template definition of the storage.Volume.
VolumeTemplate *storage.VolumeTemplateSpec
}
27 changes: 27 additions & 0 deletions apis/compute/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ type VolumeSource struct {
VolumeRef *corev1.LocalObjectReference
// EmptyDisk instructs to use a Volume offered by the machine pool provider.
EmptyDisk *EmptyDiskVolumeSource
// Ephemeral instructs to create an ephemeral (i.e. coupled to the lifetime of the surrounding object)
// Volume to use.
Ephemeral *EphemeralVolumeSource
}

// EmptyDiskVolumeSource is a volume that's offered by the machine pool provider.
Expand All @@ -109,20 +112,44 @@ type EmptyDiskVolumeSource struct {
type NetworkInterfaceStatus struct {
// Name is the name of the NetworkInterface to whom the status belongs to.
Name string
// Phase is the NetworkInterface binding phase of the NetworkInterface.
Phase NetworkInterfacePhase
// IPs are the ips allocated for the network interface.
IPs []commonv1alpha1.IP
// VirtualIP is the virtual ip allocated for the network interface.
VirtualIP *commonv1alpha1.IP
}

// NetworkInterfacePhase represents the binding phase a NetworkInterface can be in.
type NetworkInterfacePhase string

const (
// NetworkInterfacePhasePending is used for a NetworkInterface that is not yet bound.
NetworkInterfacePhasePending NetworkInterfacePhase = "Pending"
// NetworkInterfacePhaseBound is used for a NetworkInterface that is bound.
NetworkInterfacePhaseBound NetworkInterfacePhase = "Bound"
)

// VolumeStatus is the status of a Volume.
type VolumeStatus struct {
// Name is the name of a volume attachment.
Name string
// Phase represents the binding phase of a Volume.
Phase VolumePhase
// DeviceID is the disk device ID on the host.
DeviceID string
}

// VolumePhase represents the binding phase a Volume can be in.
type VolumePhase string

const (
// VolumePhasePending is used for a Volume that is not yet bound.
VolumePhasePending VolumePhase = "Pending"
// VolumePhaseBound is used for a Volume that is bound.
VolumePhaseBound VolumePhase = "Bound"
)

// MachineStatus defines the observed state of Machine
type MachineStatus struct {
// State is the state of the machine.
Expand Down
8 changes: 8 additions & 0 deletions apis/compute/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package v1alpha1

import (
networkingv1alpha1 "github.com/onmetal/onmetal-api/apis/networking/v1alpha1"
storagev1alpha1 "github.com/onmetal/onmetal-api/apis/storage/v1alpha1"
)

// EphemeralNetworkInterfaceSource is a definition for an ephemeral (i.e. coupled to the lifetime of the surrounding
Expand All @@ -24,3 +25,10 @@ type EphemeralNetworkInterfaceSource struct {
// NetworkInterfaceTemplate is the template definition of the networking.NetworkInterface.
NetworkInterfaceTemplate *networkingv1alpha1.NetworkInterfaceTemplateSpec `json:"networkInterfaceTemplate,omitempty"`
}

// EphemeralVolumeSource is a definition for an ephemeral (i.e. coupled to the lifetime of the surrounding object)
// storage.Volume.
type EphemeralVolumeSource struct {
// VolumeTemplate is the template definition of the storage.Volume.
VolumeTemplate *storagev1alpha1.VolumeTemplateSpec `json:"volumeTemplate,omitempty"`
}
27 changes: 27 additions & 0 deletions apis/compute/v1alpha1/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ type VolumeSource struct {
VolumeRef *corev1.LocalObjectReference `json:"volumeRef,omitempty"`
// EmptyDisk instructs to use a Volume offered by the machine pool provider.
EmptyDisk *EmptyDiskVolumeSource `json:"emptyDisk,omitempty"`
// Ephemeral instructs to create an ephemeral (i.e. coupled to the lifetime of the surrounding object)
// Volume to use.
Ephemeral *EphemeralVolumeSource `json:"ephemeral,omitempty"`
}

// EmptyDiskVolumeSource is a volume that's offered by the machine pool provider.
Expand All @@ -109,20 +112,44 @@ type EmptyDiskVolumeSource struct {
type NetworkInterfaceStatus struct {
// Name is the name of the NetworkInterface to whom the status belongs to.
Name string `json:"name"`
// Phase is the NetworkInterface binding phase of the NetworkInterface.
Phase NetworkInterfacePhase `json:"phase,omitempty"`
// IPs are the ips allocated for the network interface.
IPs []commonv1alpha1.IP `json:"ips,omitempty"`
// VirtualIP is the virtual ip allocated for the network interface.
VirtualIP *commonv1alpha1.IP `json:"virtualIP,omitempty"`
}

// NetworkInterfacePhase represents the binding phase a NetworkInterface can be in.
type NetworkInterfacePhase string

const (
// NetworkInterfacePhasePending is used for a NetworkInterface that is not yet bound.
NetworkInterfacePhasePending NetworkInterfacePhase = "Pending"
// NetworkInterfacePhaseBound is used for a NetworkInterface that is bound.
NetworkInterfacePhaseBound NetworkInterfacePhase = "Bound"
)

// VolumeStatus is the status of a Volume.
type VolumeStatus struct {
// Name is the name of a volume attachment.
Name string `json:"name"`
// Phase represents the binding phase of a Volume.
Phase VolumePhase `json:"phase,omitempty"`
// DeviceID is the disk device ID on the host.
DeviceID string `json:"deviceID,omitempty"`
}

// VolumePhase represents the binding phase a Volume can be in.
type VolumePhase string

const (
// VolumePhasePending is used for a Volume that is not yet bound.
VolumePhasePending VolumePhase = "Pending"
// VolumePhaseBound is used for a Volume that is bound.
VolumePhaseBound VolumePhase = "Bound"
)

// MachineStatus defines the observed state of Machine
type MachineStatus struct {
// State is the state of the machine.
Expand Down
38 changes: 38 additions & 0 deletions apis/compute/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions apis/compute/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions apis/compute/validation/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package validation
import (
onmetalapivalidation "github.com/onmetal/onmetal-api/api/validation"
"github.com/onmetal/onmetal-api/apis/compute"
"github.com/onmetal/onmetal-api/apis/storage"
storagevalidation "github.com/onmetal/onmetal-api/apis/storage/validation"
corev1 "k8s.io/api/core/v1"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
Expand Down Expand Up @@ -117,6 +119,14 @@ func validateVolumeSource(source *compute.VolumeSource, fldPath *field.Path) fie
allErrs = append(allErrs, validateEmptyDiskVolumeSource(source.EmptyDisk, fldPath.Child("emptyDisk"))...)
}
}
if source.Ephemeral != nil {
if numDefs > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("ephemeral"), "must only specify one volume source"))
} else {
numDefs++
allErrs = append(allErrs, validateEphemeralVolumeSource(source.Ephemeral, fldPath.Child("ephemeral"))...)
}
}
if numDefs == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, source, "must specify at least one volume source"))
}
Expand All @@ -134,6 +144,37 @@ func validateEmptyDiskVolumeSource(source *compute.EmptyDiskVolumeSource, fldPat
return allErrs
}

func validateEphemeralVolumeSource(source *compute.EphemeralVolumeSource, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList

if source.VolumeTemplate == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeTemplate"), "must specify volume template"))
} else {
allErrs = append(allErrs, validateVolumeTemplateSpecForMachine(source.VolumeTemplate, fldPath.Child("volumeTemplate"))...)
}

return allErrs
}

func validateVolumeTemplateSpecForMachine(template *storage.VolumeTemplateSpec, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList

if template == nil {
allErrs = append(allErrs, field.Required(fldPath, ""))
} else {
allErrs = append(allErrs, storagevalidation.ValidateVolumeTemplateSpec(template, fldPath)...)

if template.Spec.ClaimRef != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "claimRef"), "may not specify claimRef"))
}
if template.Spec.Unclaimable {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "unclaimable"), "may not specify unclaimable"))
}
}

return allErrs
}

// validateMachineSpecUpdate validates the spec of a Machine object before an update.
func validateMachineSpecUpdate(new, old *compute.MachineSpec, deletionTimestampSet bool, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
Expand Down
Loading

0 comments on commit f772a16

Please sign in to comment.