mirror of
https://github.com/getcompanion-ai/computer-host.git
synced 2026-04-15 07:04:43 +00:00
chore: zsh prompt alignment
This commit is contained in:
parent
3eb610b703
commit
39f8882c30
8 changed files with 56 additions and 11 deletions
|
|
@ -17,7 +17,7 @@ type Machine struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuestConfig struct {
|
type GuestConfig struct {
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
AuthorizedKeys []string `json:"authorized_keys,omitempty"`
|
AuthorizedKeys []string `json:"authorized_keys,omitempty"`
|
||||||
TrustedUserCAKeys []string `json:"trusted_user_ca_keys,omitempty"`
|
TrustedUserCAKeys []string `json:"trusted_user_ca_keys,omitempty"`
|
||||||
LoginWebhook *GuestLoginWebhook `json:"login_webhook,omitempty"`
|
LoginWebhook *GuestLoginWebhook `json:"login_webhook,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ type Daemon struct {
|
||||||
store store.Store
|
store store.Store
|
||||||
runtime Runtime
|
runtime Runtime
|
||||||
|
|
||||||
reconfigureGuestIdentity func(context.Context, string, contracthost.MachineID) error
|
reconfigureGuestIdentity func(context.Context, string, contracthost.MachineID, *contracthost.GuestConfig) error
|
||||||
readGuestSSHPublicKey func(context.Context, string) (string, error)
|
readGuestSSHPublicKey func(context.Context, string) (string, error)
|
||||||
syncGuestFilesystem func(context.Context, string) error
|
syncGuestFilesystem func(context.Context, string) error
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ func TestCreateMachineStagesArtifactsAndPersistsState(t *testing.T) {
|
||||||
RootFSURL: server.URL + "/rootfs",
|
RootFSURL: server.URL + "/rootfs",
|
||||||
},
|
},
|
||||||
GuestConfig: &contracthost.GuestConfig{
|
GuestConfig: &contracthost.GuestConfig{
|
||||||
|
Hostname: "workbox",
|
||||||
AuthorizedKeys: []string{
|
AuthorizedKeys: []string{
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOverrideKey daemon-test",
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestOverrideKey daemon-test",
|
||||||
},
|
},
|
||||||
|
|
@ -179,7 +180,7 @@ func TestCreateMachineStagesArtifactsAndPersistsState(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("mmds payload type mismatch: got %T", runtime.lastSpec.MMDS.Data)
|
t.Fatalf("mmds payload type mismatch: got %T", runtime.lastSpec.MMDS.Data)
|
||||||
}
|
}
|
||||||
if payload.Latest.MetaData.Hostname != "vm-1" {
|
if payload.Latest.MetaData.Hostname != "workbox" {
|
||||||
t.Fatalf("mmds hostname mismatch: got %q", payload.Latest.MetaData.Hostname)
|
t.Fatalf("mmds hostname mismatch: got %q", payload.Latest.MetaData.Hostname)
|
||||||
}
|
}
|
||||||
authorizedKeys := strings.Join(payload.Latest.MetaData.AuthorizedKeys, "\n")
|
authorizedKeys := strings.Join(payload.Latest.MetaData.AuthorizedKeys, "\n")
|
||||||
|
|
@ -340,7 +341,7 @@ func TestRestoreSnapshotFallsBackToLocalSnapshotNetwork(t *testing.T) {
|
||||||
t.Fatalf("create daemon: %v", err)
|
t.Fatalf("create daemon: %v", err)
|
||||||
}
|
}
|
||||||
stubGuestSSHPublicKeyReader(hostDaemon)
|
stubGuestSSHPublicKeyReader(hostDaemon)
|
||||||
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID) error { return nil }
|
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID, *contracthost.GuestConfig) error { return nil }
|
||||||
|
|
||||||
artifactRef := contracthost.ArtifactRef{KernelImageURL: "kernel", RootFSURL: "rootfs"}
|
artifactRef := contracthost.ArtifactRef{KernelImageURL: "kernel", RootFSURL: "rootfs"}
|
||||||
kernelPath := filepath.Join(root, "artifact-kernel")
|
kernelPath := filepath.Join(root, "artifact-kernel")
|
||||||
|
|
@ -397,6 +398,7 @@ func TestRestoreSnapshotFallsBackToLocalSnapshotNetwork(t *testing.T) {
|
||||||
{ID: "disk-system", Kind: contracthost.SnapshotArtifactKindDisk, Name: "system.img", DownloadURL: server.URL + "/system"},
|
{ID: "disk-system", Kind: contracthost.SnapshotArtifactKindDisk, Name: "system.img", DownloadURL: server.URL + "/system"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GuestConfig: &contracthost.GuestConfig{Hostname: "restored-shell"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("restore snapshot: %v", err)
|
t.Fatalf("restore snapshot: %v", err)
|
||||||
|
|
@ -462,9 +464,11 @@ func TestRestoreSnapshotUsesDurableSnapshotSpec(t *testing.T) {
|
||||||
stubGuestSSHPublicKeyReader(hostDaemon)
|
stubGuestSSHPublicKeyReader(hostDaemon)
|
||||||
var reconfiguredHost string
|
var reconfiguredHost string
|
||||||
var reconfiguredMachine contracthost.MachineID
|
var reconfiguredMachine contracthost.MachineID
|
||||||
hostDaemon.reconfigureGuestIdentity = func(_ context.Context, host string, machineID contracthost.MachineID) error {
|
var reconfiguredConfig *contracthost.GuestConfig
|
||||||
|
hostDaemon.reconfigureGuestIdentity = func(_ context.Context, host string, machineID contracthost.MachineID, guestConfig *contracthost.GuestConfig) error {
|
||||||
reconfiguredHost = host
|
reconfiguredHost = host
|
||||||
reconfiguredMachine = machineID
|
reconfiguredMachine = machineID
|
||||||
|
reconfiguredConfig = cloneGuestConfig(guestConfig)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,6 +501,7 @@ func TestRestoreSnapshotUsesDurableSnapshotSpec(t *testing.T) {
|
||||||
{ID: "disk-user-0", Kind: contracthost.SnapshotArtifactKindDisk, Name: "user-0.img", DownloadURL: server.URL + "/user-0"},
|
{ID: "disk-user-0", Kind: contracthost.SnapshotArtifactKindDisk, Name: "user-0.img", DownloadURL: server.URL + "/user-0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GuestConfig: &contracthost.GuestConfig{Hostname: "restored-shell"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("restore snapshot: %v", err)
|
t.Fatalf("restore snapshot: %v", err)
|
||||||
|
|
@ -525,6 +530,9 @@ func TestRestoreSnapshotUsesDurableSnapshotSpec(t *testing.T) {
|
||||||
if reconfiguredHost != "127.0.0.1" || reconfiguredMachine != "restored" {
|
if reconfiguredHost != "127.0.0.1" || reconfiguredMachine != "restored" {
|
||||||
t.Fatalf("guest identity reconfigure mismatch: host=%q machine=%q", reconfiguredHost, reconfiguredMachine)
|
t.Fatalf("guest identity reconfigure mismatch: host=%q machine=%q", reconfiguredHost, reconfiguredMachine)
|
||||||
}
|
}
|
||||||
|
if reconfiguredConfig == nil || reconfiguredConfig.Hostname != "restored-shell" {
|
||||||
|
t.Fatalf("guest identity hostname mismatch: %#v", reconfiguredConfig)
|
||||||
|
}
|
||||||
|
|
||||||
machine, err := fileStore.GetMachine(context.Background(), "restored")
|
machine, err := fileStore.GetMachine(context.Background(), "restored")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -251,9 +251,13 @@ func (d *Daemon) mergedGuestConfig(config *contracthost.GuestConfig) (*contracth
|
||||||
}
|
}
|
||||||
|
|
||||||
merged := &contracthost.GuestConfig{
|
merged := &contracthost.GuestConfig{
|
||||||
|
Hostname: "",
|
||||||
AuthorizedKeys: authorizedKeys,
|
AuthorizedKeys: authorizedKeys,
|
||||||
TrustedUserCAKeys: nil,
|
TrustedUserCAKeys: nil,
|
||||||
}
|
}
|
||||||
|
if config != nil {
|
||||||
|
merged.Hostname = strings.TrimSpace(config.Hostname)
|
||||||
|
}
|
||||||
if strings.TrimSpace(d.config.GuestLoginCAPublicKey) != "" {
|
if strings.TrimSpace(d.config.GuestLoginCAPublicKey) != "" {
|
||||||
merged.TrustedUserCAKeys = append(merged.TrustedUserCAKeys, d.config.GuestLoginCAPublicKey)
|
merged.TrustedUserCAKeys = append(merged.TrustedUserCAKeys, d.config.GuestLoginCAPublicKey)
|
||||||
}
|
}
|
||||||
|
|
@ -447,6 +451,21 @@ func validateGuestConfig(config *contracthost.GuestConfig) error {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if config.Hostname != "" {
|
||||||
|
hostname := strings.TrimSpace(config.Hostname)
|
||||||
|
if hostname == "" {
|
||||||
|
return fmt.Errorf("guest_config.hostname is required")
|
||||||
|
}
|
||||||
|
if len(hostname) > 63 {
|
||||||
|
return fmt.Errorf("guest_config.hostname must be 63 characters or fewer")
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(hostname, "/\\") {
|
||||||
|
return fmt.Errorf("guest_config.hostname must not contain path separators")
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(hostname, " \t\r\n") {
|
||||||
|
return fmt.Errorf("guest_config.hostname must not contain whitespace")
|
||||||
|
}
|
||||||
|
}
|
||||||
for i, key := range config.AuthorizedKeys {
|
for i, key := range config.AuthorizedKeys {
|
||||||
if strings.TrimSpace(key) == "" {
|
if strings.TrimSpace(key) == "" {
|
||||||
return fmt.Errorf("guest_config.authorized_keys[%d] is required", i)
|
return fmt.Errorf("guest_config.authorized_keys[%d] is required", i)
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import (
|
||||||
contracthost "github.com/getcompanion-ai/computer-host/contract"
|
contracthost "github.com/getcompanion-ai/computer-host/contract"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *Daemon) reconfigureGuestIdentityOverSSH(ctx context.Context, runtimeHost string, machineID contracthost.MachineID) error {
|
func (d *Daemon) reconfigureGuestIdentityOverSSH(ctx context.Context, runtimeHost string, machineID contracthost.MachineID, guestConfig *contracthost.GuestConfig) error {
|
||||||
runtimeHost = strings.TrimSpace(runtimeHost)
|
runtimeHost = strings.TrimSpace(runtimeHost)
|
||||||
machineName := strings.TrimSpace(string(machineID))
|
machineName := guestHostname(machineID, guestConfig)
|
||||||
if runtimeHost == "" {
|
if runtimeHost == "" {
|
||||||
return fmt.Errorf("guest runtime host is required")
|
return fmt.Errorf("guest runtime host is required")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ func cloneGuestConfig(config *contracthost.GuestConfig) *contracthost.GuestConfi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cloned := &contracthost.GuestConfig{
|
cloned := &contracthost.GuestConfig{
|
||||||
|
Hostname: config.Hostname,
|
||||||
AuthorizedKeys: append([]string(nil), config.AuthorizedKeys...),
|
AuthorizedKeys: append([]string(nil), config.AuthorizedKeys...),
|
||||||
TrustedUserCAKeys: append([]string(nil), config.TrustedUserCAKeys...),
|
TrustedUserCAKeys: append([]string(nil), config.TrustedUserCAKeys...),
|
||||||
}
|
}
|
||||||
|
|
@ -45,8 +46,17 @@ func cloneGuestConfig(config *contracthost.GuestConfig) *contracthost.GuestConfi
|
||||||
return cloned
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func guestHostname(machineID contracthost.MachineID, guestConfig *contracthost.GuestConfig) string {
|
||||||
|
if guestConfig != nil {
|
||||||
|
if hostname := strings.TrimSpace(guestConfig.Hostname); hostname != "" {
|
||||||
|
return hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(machineID))
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Daemon) guestMetadataSpec(machineID contracthost.MachineID, guestConfig *contracthost.GuestConfig) (*firecracker.MMDSSpec, error) {
|
func (d *Daemon) guestMetadataSpec(machineID contracthost.MachineID, guestConfig *contracthost.GuestConfig) (*firecracker.MMDSSpec, error) {
|
||||||
name := strings.TrimSpace(string(machineID))
|
name := guestHostname(machineID, guestConfig)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, fmt.Errorf("machine id is required")
|
return nil, fmt.Errorf("machine id is required")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -448,7 +448,7 @@ func TestRestoreSnapshotDeletesSystemVolumeRecordWhenRelayAllocationFails(t *tes
|
||||||
t.Fatalf("create daemon: %v", err)
|
t.Fatalf("create daemon: %v", err)
|
||||||
}
|
}
|
||||||
stubGuestSSHPublicKeyReader(hostDaemon)
|
stubGuestSSHPublicKeyReader(hostDaemon)
|
||||||
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID) error { return nil }
|
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID, *contracthost.GuestConfig) error { return nil }
|
||||||
|
|
||||||
artifactRef := contracthost.ArtifactRef{KernelImageURL: "kernel", RootFSURL: "rootfs"}
|
artifactRef := contracthost.ArtifactRef{KernelImageURL: "kernel", RootFSURL: "rootfs"}
|
||||||
kernelPath := filepath.Join(root, "artifact-kernel")
|
kernelPath := filepath.Join(root, "artifact-kernel")
|
||||||
|
|
@ -695,7 +695,7 @@ func TestRestoreSnapshotCleansStagingArtifactsAfterSuccess(t *testing.T) {
|
||||||
t.Fatalf("create daemon: %v", err)
|
t.Fatalf("create daemon: %v", err)
|
||||||
}
|
}
|
||||||
stubGuestSSHPublicKeyReader(hostDaemon)
|
stubGuestSSHPublicKeyReader(hostDaemon)
|
||||||
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID) error { return nil }
|
hostDaemon.reconfigureGuestIdentity = func(context.Context, string, contracthost.MachineID, *contracthost.GuestConfig) error { return nil }
|
||||||
|
|
||||||
server := newRestoreArtifactServer(t, map[string][]byte{
|
server := newRestoreArtifactServer(t, map[string][]byte{
|
||||||
"/kernel": []byte("kernel"),
|
"/kernel": []byte("kernel"),
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,13 @@ func (d *Daemon) RestoreSnapshot(ctx context.Context, snapshotID contracthost.Sn
|
||||||
if err := validateArtifactRef(req.Artifact); err != nil {
|
if err := validateArtifactRef(req.Artifact); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := validateGuestConfig(req.GuestConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
guestConfig, err := d.mergedGuestConfig(req.GuestConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
unlock := d.lockMachine(req.MachineID)
|
unlock := d.lockMachine(req.MachineID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
@ -349,7 +356,7 @@ func (d *Daemon) RestoreSnapshot(ctx context.Context, snapshotID contracthost.Sn
|
||||||
clearOperation = true
|
clearOperation = true
|
||||||
return nil, fmt.Errorf("wait for restored guest ready: %w", err)
|
return nil, fmt.Errorf("wait for restored guest ready: %w", err)
|
||||||
}
|
}
|
||||||
if err := d.reconfigureGuestIdentity(ctx, machineState.RuntimeHost, req.MachineID); err != nil {
|
if err := d.reconfigureGuestIdentity(ctx, machineState.RuntimeHost, req.MachineID, guestConfig); err != nil {
|
||||||
_ = d.runtime.Delete(ctx, *machineState)
|
_ = d.runtime.Delete(ctx, *machineState)
|
||||||
_ = os.RemoveAll(filepath.Dir(newSystemDiskPath))
|
_ = os.RemoveAll(filepath.Dir(newSystemDiskPath))
|
||||||
clearOperation = true
|
clearOperation = true
|
||||||
|
|
@ -406,6 +413,7 @@ func (d *Daemon) RestoreSnapshot(ctx context.Context, snapshotID contracthost.Sn
|
||||||
machineRecord := model.MachineRecord{
|
machineRecord := model.MachineRecord{
|
||||||
ID: req.MachineID,
|
ID: req.MachineID,
|
||||||
Artifact: req.Artifact,
|
Artifact: req.Artifact,
|
||||||
|
GuestConfig: cloneGuestConfig(guestConfig),
|
||||||
SystemVolumeID: systemVolumeID,
|
SystemVolumeID: systemVolumeID,
|
||||||
UserVolumeIDs: restoredUserVolumeIDs,
|
UserVolumeIDs: restoredUserVolumeIDs,
|
||||||
RuntimeHost: machineState.RuntimeHost,
|
RuntimeHost: machineState.RuntimeHost,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue