mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
feat: block device mappings
This commit is contained in:
@@ -24,6 +24,11 @@
|
||||
- Confirm cloud-init will resize volume on boot and configured correctly.
|
||||
- Attach additional volumes
|
||||
- Attach to VPC / Security group (required Open vSwitch implementation)
|
||||
- only allow hive to use x% of cpu and memory. dont allow allocating 100% of resources.
|
||||
- placement group instances, allow spreading instance group between nodes.
|
||||
- capabilities flag
|
||||
- more instance types - per cpu gen
|
||||
- multi part uploads on frontend
|
||||
|
||||
## EC2 describe-instances
|
||||
|
||||
|
||||
+14
-1
@@ -1420,7 +1420,7 @@ func (d *Daemon) handleEC2RunInstances(msg *nats.Msg) {
|
||||
var successCount int
|
||||
for _, instance := range instances {
|
||||
// Prepare the root volume, cloud-init, EFI drives via NBD (AMI clone to new volume)
|
||||
err = d.instanceService.GenerateVolumes(runInstancesInput, instance)
|
||||
volumeInfos, err := d.instanceService.GenerateVolumes(runInstancesInput, instance)
|
||||
if err != nil {
|
||||
slog.Error("handleEC2RunInstances GenerateVolumes failed", "instanceId", instance.ID, "err", err)
|
||||
d.resourceMgr.deallocate(instanceType)
|
||||
@@ -1428,6 +1428,19 @@ func (d *Daemon) handleEC2RunInstances(msg *nats.Msg) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Populate BlockDeviceMappings on the ec2.Instance
|
||||
instance.Instance.BlockDeviceMappings = make([]*ec2.InstanceBlockDeviceMapping, 0, len(volumeInfos))
|
||||
for _, vi := range volumeInfos {
|
||||
mapping := &ec2.InstanceBlockDeviceMapping{}
|
||||
mapping.SetDeviceName(vi.DeviceName)
|
||||
mapping.Ebs = &ec2.EbsInstanceBlockDevice{}
|
||||
mapping.Ebs.SetVolumeId(vi.VolumeId)
|
||||
mapping.Ebs.SetAttachTime(vi.AttachTime)
|
||||
mapping.Ebs.SetDeleteOnTermination(vi.DeleteOnTermination)
|
||||
mapping.Ebs.SetStatus("attached")
|
||||
instance.Instance.BlockDeviceMappings = append(instance.Instance.BlockDeviceMappings, mapping)
|
||||
}
|
||||
|
||||
// Launch the instance infrastructure (QEMU, QMP, NATS subscriptions)
|
||||
err = d.LaunchInstance(instance)
|
||||
if err != nil {
|
||||
|
||||
@@ -75,6 +75,15 @@ type CloudInitMetaData struct {
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// VolumeInfo holds volume information returned from GenerateVolumes
|
||||
// for populating BlockDeviceMappings in the EC2 API response
|
||||
type VolumeInfo struct {
|
||||
VolumeId string
|
||||
DeviceName string
|
||||
AttachTime time.Time
|
||||
DeleteOnTermination bool
|
||||
}
|
||||
|
||||
// InstanceServiceImpl handles daemon-side EC2 instance operations
|
||||
type InstanceServiceImpl struct {
|
||||
config *config.Config
|
||||
@@ -132,28 +141,43 @@ func (s *InstanceServiceImpl) RunInstance(input *ec2.RunInstancesInput) (*vm.VM,
|
||||
return instance, ec2Instance, nil
|
||||
}
|
||||
|
||||
func (s *InstanceServiceImpl) GenerateVolumes(input *ec2.RunInstancesInput, instance *vm.VM) (err error) {
|
||||
func (s *InstanceServiceImpl) GenerateVolumes(input *ec2.RunInstancesInput, instance *vm.VM) ([]VolumeInfo, error) {
|
||||
|
||||
var size int = 4 * 1024 * 1024 * 1024 // 4GB default size
|
||||
var deviceName string
|
||||
var deviceName string = "/dev/xvda" // Default device name
|
||||
var volumeType string
|
||||
var iops int
|
||||
var imageId string
|
||||
var snapshotId string
|
||||
var deleteOnTermination bool = true // Default to true
|
||||
|
||||
// Handle block device mappings
|
||||
if len(input.BlockDeviceMappings) > 0 {
|
||||
size = int(*input.BlockDeviceMappings[0].Ebs.VolumeSize)
|
||||
deviceName = *input.BlockDeviceMappings[0].DeviceName
|
||||
volumeType = *input.BlockDeviceMappings[0].Ebs.VolumeType
|
||||
iops = int(*input.BlockDeviceMappings[0].Ebs.Iops)
|
||||
bdm := input.BlockDeviceMappings[0]
|
||||
if bdm.DeviceName != nil {
|
||||
deviceName = *bdm.DeviceName
|
||||
}
|
||||
if bdm.Ebs != nil {
|
||||
if bdm.Ebs.VolumeSize != nil {
|
||||
size = int(*bdm.Ebs.VolumeSize)
|
||||
}
|
||||
if bdm.Ebs.VolumeType != nil {
|
||||
volumeType = *bdm.Ebs.VolumeType
|
||||
}
|
||||
if bdm.Ebs.Iops != nil {
|
||||
iops = int(*bdm.Ebs.Iops)
|
||||
}
|
||||
if bdm.Ebs.DeleteOnTermination != nil {
|
||||
deleteOnTermination = *bdm.Ebs.DeleteOnTermination
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine image ID and snapshot ID
|
||||
if strings.HasPrefix(*input.ImageId, "ami-") {
|
||||
randomNumber, err := rand.Int(rand.Reader, big.NewInt(100_000_000))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
imageId = viperblock.GenerateVolumeID("vol", fmt.Sprintf("%d-%s", randomNumber, *input.ImageId), "predastore", time.Now().Unix())
|
||||
snapshotId = *input.ImageId
|
||||
@@ -161,11 +185,14 @@ func (s *InstanceServiceImpl) GenerateVolumes(input *ec2.RunInstancesInput, inst
|
||||
imageId = *input.ImageId
|
||||
}
|
||||
|
||||
// Capture attach time for the root volume
|
||||
attachTime := time.Now()
|
||||
|
||||
volumeConfig := viperblock.VolumeConfig{
|
||||
VolumeMetadata: viperblock.VolumeMetadata{
|
||||
VolumeID: imageId,
|
||||
SizeGiB: utils.SafeIntToUint64(size / 1024 / 1024 / 1024),
|
||||
CreatedAt: time.Now(),
|
||||
CreatedAt: attachTime,
|
||||
DeviceName: deviceName,
|
||||
VolumeType: volumeType,
|
||||
IOPS: iops,
|
||||
@@ -174,26 +201,36 @@ func (s *InstanceServiceImpl) GenerateVolumes(input *ec2.RunInstancesInput, inst
|
||||
}
|
||||
|
||||
// Step 1: Create or validate root volume
|
||||
err = s.prepareRootVolume(input, imageId, size, volumeConfig, instance)
|
||||
err := s.prepareRootVolume(input, imageId, size, volumeConfig, instance)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 2: Create EFI partition
|
||||
err = s.prepareEFIVolume(imageId, volumeConfig, instance)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 3: Create cloud-init volume if needed
|
||||
if input.KeyName != nil && *input.KeyName != "" || (input.UserData != nil && *input.UserData != "") {
|
||||
err = s.prepareCloudInitVolume(input, imageId, volumeConfig, instance)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// Return volume info for the root volume only (EFI and cloud-init are internal)
|
||||
volumeInfos := []VolumeInfo{
|
||||
{
|
||||
VolumeId: imageId,
|
||||
DeviceName: deviceName,
|
||||
AttachTime: attachTime,
|
||||
DeleteOnTermination: deleteOnTermination,
|
||||
},
|
||||
}
|
||||
|
||||
return volumeInfos, nil
|
||||
|
||||
}
|
||||
|
||||
|
||||
+36
@@ -114,6 +114,42 @@ function InstanceDetail() {
|
||||
</DetailCard.Content>
|
||||
</DetailCard> */}
|
||||
|
||||
{/* Block Device Mappings */}
|
||||
{instance.BlockDeviceMappings &&
|
||||
instance.BlockDeviceMappings.length > 0 && (
|
||||
<DetailCard>
|
||||
<DetailCard.Header>Block Device Mappings</DetailCard.Header>
|
||||
<DetailCard.Content>
|
||||
<DetailRow
|
||||
label="Device Name"
|
||||
value={instance.BlockDeviceMappings[0]?.DeviceName}
|
||||
/>
|
||||
<DetailRow
|
||||
label="Volume ID"
|
||||
value={instance.BlockDeviceMappings[0]?.Ebs?.VolumeId}
|
||||
/>
|
||||
<DetailRow
|
||||
label="Status"
|
||||
value={instance.BlockDeviceMappings[0]?.Ebs?.Status}
|
||||
/>
|
||||
<DetailRow
|
||||
label="Attach Time"
|
||||
value={formatDateTime(
|
||||
instance.BlockDeviceMappings[0]?.Ebs?.AttachTime,
|
||||
)}
|
||||
/>
|
||||
<DetailRow
|
||||
label="Delete on Termination"
|
||||
value={
|
||||
instance.BlockDeviceMappings[0]?.Ebs?.DeleteOnTermination
|
||||
? "Yes"
|
||||
: "No"
|
||||
}
|
||||
/>
|
||||
</DetailCard.Content>
|
||||
</DetailCard>
|
||||
)}
|
||||
|
||||
{/* AMI Details */}
|
||||
{image && <AmiDetails image={image} />}
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,8 @@ rm -rf ~/hive/viperblock/*
|
||||
|
||||
rm -rf ~/hive/nats/*
|
||||
|
||||
rm -rf ~/hive/images/*
|
||||
|
||||
# Enable pprof for development
|
||||
PPROF_ENABLED=1 PPROF_OUTPUT=/tmp/hive-vm.prof ./scripts/start-dev.sh
|
||||
|
||||
|
||||
+17
-6
@@ -4,6 +4,10 @@
|
||||
# This script starts all required services in the correct order using Hive service commands
|
||||
# Usage: ./scripts/start-dev.sh [data-dir]
|
||||
# data-dir: Optional data directory path (default: ~/hive)
|
||||
#
|
||||
# Environment variables:
|
||||
# UI=false Skip starting Hive UI (e.g., UI=false ./scripts/start-dev.sh)
|
||||
# HIVE_SKIP_BUILD=true Skip building binaries before starting
|
||||
|
||||
set -e
|
||||
|
||||
@@ -270,18 +274,25 @@ start_service "awsgw" "$AWSGW_CMD"
|
||||
#check_service "awsgw" "9999"
|
||||
|
||||
|
||||
# 6️⃣ Start Hive UI
|
||||
echo ""
|
||||
echo "6️⃣. Starting Hive UI..."
|
||||
# 6️⃣ Start Hive UI (skip with UI=false)
|
||||
if [ "${UI}" != "false" ]; then
|
||||
echo ""
|
||||
echo "6️⃣. Starting Hive UI..."
|
||||
|
||||
HIVEUI_CMD="./bin/hive service hive-ui start"
|
||||
HIVEUI_CMD="./bin/hive service hive-ui start"
|
||||
|
||||
start_service "hive-ui" "$HIVEUI_CMD"
|
||||
start_service "hive-ui" "$HIVEUI_CMD"
|
||||
else
|
||||
echo ""
|
||||
echo "6️⃣. Skipping Hive UI (UI=false)"
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo "🔗 Service endpoints will be:"
|
||||
echo " - Hive UI: https://localhost:3000"
|
||||
if [ "${UI}" != "false" ]; then
|
||||
echo " - Hive UI: https://localhost:3000"
|
||||
fi
|
||||
echo " - NATS: nats://localhost:4222"
|
||||
echo " - Predastore: https://localhost:8443"
|
||||
echo " - AWS Gateway: https://localhost:9999"
|
||||
|
||||
Reference in New Issue
Block a user