From 29ae807d122134ec46326bc602e65c96a9aaf20b Mon Sep 17 00:00:00 2001 From: saffronjam Date: Tue, 17 Oct 2023 15:17:12 +0200 Subject: [PATCH 1/3] fix ssh port not created if no ports were supplied in params --- models/sys/vm/converters.go | 12 ++++++++++++ service/vm_service/vm_service.go | 16 ---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/models/sys/vm/converters.go b/models/sys/vm/converters.go index bf817987..ae53320f 100644 --- a/models/sys/vm/converters.go +++ b/models/sys/vm/converters.go @@ -67,6 +67,12 @@ func (p *CreateParams) FromDTO(dto *body.VmCreate, fallbackZone *string, deploym p.Ports = append(p.Ports, fromDtoPort(&port)) } + p.Ports = append(p.Ports, Port{ + Name: "__ssh", + Port: 22, + Protocol: "tcp", + }) + p.CpuCores = dto.CpuCores p.RAM = dto.RAM p.DiskSize = dto.DiskSize @@ -95,6 +101,12 @@ func (p *UpdateParams) FromDTO(dto *body.VmUpdate) { ports = append(ports, fromDtoPort(&port)) } + ports = append(ports, Port{ + Name: "__ssh", + Port: 22, + Protocol: "tcp", + }) + p.Ports = &ports } else { p.Ports = nil diff --git a/service/vm_service/vm_service.go b/service/vm_service/vm_service.go index 4eeaeaec..526b7b8b 100644 --- a/service/vm_service/vm_service.go +++ b/service/vm_service/vm_service.go @@ -28,10 +28,6 @@ func Create(id, ownerID string, vmCreate *body.VmCreate) error { params := &vmModel.CreateParams{} params.FromDTO(vmCreate, &fallback, &deploymentZone) - if params.Ports != nil { - addDeploySshToPortMap(¶ms.Ports) - } - created, err := vmModel.New().Create(id, ownerID, conf.Env.Manager, params) if err != nil { return makeError(err) @@ -68,10 +64,6 @@ func Update(vmID string, dtoVmUpdate *body.VmUpdate) error { return makeError(err) } } else { - if vmUpdate.Ports != nil { - addDeploySshToPortMap(vmUpdate.Ports) - } - err := vmModel.New().UpdateWithParamsByID(vmID, vmUpdate) if err != nil { return makeError(err) @@ -505,14 +497,6 @@ func GetExternalPortMapper(vmID string) (map[string]int, error) { return mapper, nil } -func addDeploySshToPortMap(portMap *[]vmModel.Port) { - *portMap = append(*portMap, vmModel.Port{ - Port: 22, - Name: "__ssh", - Protocol: "tcp", - }) -} - func removeDeploySshFromPortMap(portMap *[]vmModel.Port) { for i, port := range *portMap { if (port.Port == 22 || port.Name == "__ssh") && port.Protocol == "tcp" { From 1d889767db39deaaf906a0e1aadc205d26961880 Mon Sep 17 00:00:00 2001 From: saffronjam Date: Tue, 17 Oct 2023 15:31:11 +0200 Subject: [PATCH 2/3] expose vm http proxy in vm read --- models/dto/body/vm.go | 39 +++++++++++++++------------- models/sys/vm/converters.go | 38 ++++++++++++++++++++++++--- routers/api/validators/validators.go | 6 ++--- test/e2e/vms/routes_test.go | 10 +++---- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/models/dto/body/vm.go b/models/dto/body/vm.go index 62593124..fe95b471 100644 --- a/models/dto/body/vm.go +++ b/models/dto/body/vm.go @@ -4,10 +4,13 @@ import "time" type VmHttpProxy struct { Name string `json:"name" binding:"required,rfc1035,min=3,max=30"` - CustomDomain *string `json:"customDomain,omitempty" binding:"omitempty,domain_name,custom_domain,min=1,max=253"` + CustomDomain *string `json:"customDomain,omitempty" binding:"omitempty,domain_name,min=1,max=253"` + + URL *string `json:"url,omitempty" ` + CustomDomainURL *string `json:"customDomainUrl,omitempty" ` } -type Port struct { +type PortRead struct { Name string `json:"name,omitempty" binding:"required,min=1,max=100"` Port int `json:"port,omitempty" binding:"required,min=1,max=65535"` ExternalPort int `json:"externalPort,omitempty"` @@ -34,31 +37,31 @@ type VmRead struct { OwnerID string `json:"ownerId"` Zone string `json:"zone"` - Specs Specs `json:"specs,omitempty"` - Ports []Port `json:"ports"` - GPU *VmGpu `json:"gpu,omitempty"` - SshPublicKey string `json:"sshPublicKey"` + Specs Specs `json:"specs,omitempty"` + Ports []PortRead `json:"ports"` + GPU *VmGpu `json:"gpu,omitempty"` + SshPublicKey string `json:"sshPublicKey"` Status string `json:"status"` ConnectionString *string `json:"connectionString,omitempty"` } type VmCreate struct { - Name string `json:"name" binding:"required,rfc1035,min=3,max=30"` - SshPublicKey string `json:"sshPublicKey" binding:"required,ssh_public_key"` - Ports []Port `json:"ports" binding:"omitempty,port_list_names,port_list_numbers,port_list_http_proxies,min=0,max=100,dive"` - CpuCores int `json:"cpuCores" binding:"required,min=2"` - RAM int `json:"ram" binding:"required,min=1"` - DiskSize int `json:"diskSize" binding:"required,min=20"` - Zone *string `json:"zone" binding:"omitempty"` + Name string `json:"name" binding:"required,rfc1035,min=3,max=30"` + SshPublicKey string `json:"sshPublicKey" binding:"required,ssh_public_key"` + Ports []PortRead `json:"ports" binding:"omitempty,port_list_names,port_list_numbers,port_list_http_proxies,min=0,max=100,dive"` + CpuCores int `json:"cpuCores" binding:"required,min=2"` + RAM int `json:"ram" binding:"required,min=1"` + DiskSize int `json:"diskSize" binding:"required,min=20"` + Zone *string `json:"zone" binding:"omitempty"` } type VmUpdate struct { - SnapshotID *string `json:"snapshotId" binding:"omitempty,uuid4"` - GpuID *string `json:"gpuId" binding:"omitempty,min=0,max=100"` - Ports *[]Port `json:"ports" binding:"omitempty,port_list_names,port_list_numbers,port_list_http_proxies,min=0,max=1000,dive"` - CpuCores *int `json:"cpuCores" binding:"omitempty,min=1"` - RAM *int `json:"ram" binding:"omitempty,min=1"` + SnapshotID *string `json:"snapshotId" binding:"omitempty,uuid4"` + GpuID *string `json:"gpuId" binding:"omitempty,min=0,max=100"` + Ports *[]PortRead `json:"ports" binding:"omitempty,port_list_names,port_list_numbers,port_list_http_proxies,min=0,max=1000,dive"` + CpuCores *int `json:"cpuCores" binding:"omitempty,min=1"` + RAM *int `json:"ram" binding:"omitempty,min=1"` } type VmCreated struct { diff --git a/models/sys/vm/converters.go b/models/sys/vm/converters.go index ae53320f..9b00cb63 100644 --- a/models/sys/vm/converters.go +++ b/models/sys/vm/converters.go @@ -1,6 +1,9 @@ package vm -import "go-deploy/models/dto/body" +import ( + "go-deploy/models/dto/body" + "go-deploy/service" +) func (vm *VM) ToDTO(status string, connectionString *string, gpu *body.GpuRead, externalPortMapper map[string]int) body.VmRead { @@ -14,7 +17,7 @@ func (vm *VM) ToDTO(status string, connectionString *string, gpu *body.GpuRead, } } - ports := make([]body.Port, 0) + ports := make([]body.PortRead, 0) if vm.Ports != nil && externalPortMapper != nil { for _, port := range vm.Ports { if port.Name == "__ssh" { @@ -26,11 +29,38 @@ func (vm *VM) ToDTO(status string, connectionString *string, gpu *body.GpuRead, continue } - ports = append(ports, body.Port{ + var url *string + if ingress := vm.Subsystems.K8s.GetIngress(vm.Name + "-" + port.Name); service.Created(ingress) { + if len(ingress.Hosts) > 0 { + urlStr := "https://" + ingress.Hosts[0] + url = &urlStr + } + } + + var customDomainUrl *string + if ingress := vm.Subsystems.K8s.GetIngress(vm.Name + "-" + port.Name + "-custom-domain"); service.Created(ingress) { + if len(ingress.Hosts) > 0 { + urlStr := "https://" + ingress.Hosts[0] + customDomainUrl = &urlStr + } + } + + var httpProxy *body.VmHttpProxy + if port.HttpProxy != nil { + httpProxy = &body.VmHttpProxy{ + Name: port.HttpProxy.Name, + CustomDomain: port.HttpProxy.CustomDomain, + URL: url, + CustomDomainURL: customDomainUrl, + } + } + + ports = append(ports, body.PortRead{ Name: port.Name, Port: port.Port, ExternalPort: externalPort, Protocol: port.Protocol, + HttpProxy: httpProxy, }) } } @@ -115,7 +145,7 @@ func (p *UpdateParams) FromDTO(dto *body.VmUpdate) { p.RAM = dto.RAM } -func fromDtoPort(port *body.Port) Port { +func fromDtoPort(port *body.PortRead) Port { var httpProxy *PortHttpProxy if port.HttpProxy != nil { httpProxy = &PortHttpProxy{ diff --git a/routers/api/validators/validators.go b/routers/api/validators/validators.go index 0dd987b3..1e922482 100644 --- a/routers/api/validators/validators.go +++ b/routers/api/validators/validators.go @@ -84,7 +84,7 @@ func EnvList(fl validator.FieldLevel) bool { } func PortListNames(fl validator.FieldLevel) bool { - portList, ok := fl.Field().Interface().([]body.Port) + portList, ok := fl.Field().Interface().([]body.PortRead) if !ok { return false } @@ -100,7 +100,7 @@ func PortListNames(fl validator.FieldLevel) bool { } func PortListNumbers(fl validator.FieldLevel) bool { - portList, ok := fl.Field().Interface().([]body.Port) + portList, ok := fl.Field().Interface().([]body.PortRead) if !ok { return false } @@ -117,7 +117,7 @@ func PortListNumbers(fl validator.FieldLevel) bool { } func PortListHttpProxies(fl validator.FieldLevel) bool { - portList, ok := fl.Field().Interface().([]body.Port) + portList, ok := fl.Field().Interface().([]body.PortRead) if !ok { return false } diff --git a/test/e2e/vms/routes_test.go b/test/e2e/vms/routes_test.go index 8f95d879..3b9f0e50 100644 --- a/test/e2e/vms/routes_test.go +++ b/test/e2e/vms/routes_test.go @@ -70,7 +70,7 @@ func TestCreate(t *testing.T) { requestBody := body.VmCreate{ Name: e2e.GenName("e2e"), SshPublicKey: publicKey, - Ports: []body.Port{ + Ports: []body.PortRead{ { Name: "e2e-test", Port: 100, @@ -119,7 +119,7 @@ func TestCreateWithInvalidBody(t *testing.T) { e2e.WithAssumedFailedVM(t, requestBody) } - invalidPorts := []body.Port{ + invalidPorts := []body.PortRead{ { Name: strings.Repeat(uuid.NewString(), 100), Port: 100, @@ -146,7 +146,7 @@ func TestCreateWithInvalidBody(t *testing.T) { requestBody := body.VmCreate{ Name: e2e.GenName("e2e"), SshPublicKey: withSshPublicKey(t), - Ports: []body.Port{ + Ports: []body.PortRead{ port, }, CpuCores: 2, @@ -229,7 +229,7 @@ func TestUpdate(t *testing.T) { requestBody := body.VmCreate{ Name: e2e.GenName("e2e"), SshPublicKey: publicKey, - Ports: []body.Port{ + Ports: []body.PortRead{ { Name: "e2e-test", Port: 100, @@ -244,7 +244,7 @@ func TestUpdate(t *testing.T) { vm := e2e.WithVM(t, requestBody) - updatedPorts := []body.Port{ + updatedPorts := []body.PortRead{ { Name: "e2e-test", Port: 100, From d88fd877d01066b893a0e3ae82f246fbb9392aab Mon Sep 17 00:00:00 2001 From: saffronjam Date: Tue, 17 Oct 2023 15:49:03 +0200 Subject: [PATCH 3/3] fix confirmers for vm k8s creation and deletion --- pkg/workers/confirm/helpers.go | 74 ++++++++++++++++++++++++++----- pkg/workers/confirm/subsystems.go | 2 + service/vm_service/vm_service.go | 11 +++-- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/pkg/workers/confirm/helpers.go b/pkg/workers/confirm/helpers.go index 1ef01e48..de16a977 100644 --- a/pkg/workers/confirm/helpers.go +++ b/pkg/workers/confirm/helpers.go @@ -4,6 +4,7 @@ import ( "fmt" "go-deploy/models/sys/deployment" "go-deploy/models/sys/vm" + "go-deploy/service" ) func appCreatedK8s(deployment *deployment.Deployment, app *deployment.App) bool { @@ -27,8 +28,8 @@ func appCreatedK8s(deployment *deployment.Deployment, app *deployment.App) bool } serviceCreated := false - for mapName, service := range deployment.Subsystems.K8s.ServiceMap { - if service.Created() && mapName == deployment.Name { + for mapName, k8sService := range deployment.Subsystems.K8s.ServiceMap { + if k8sService.Created() && mapName == deployment.Name { serviceCreated = true } } @@ -64,8 +65,8 @@ func appDeletedK8s(deployment *deployment.Deployment, app *deployment.App) bool } serviceDeleted := true - for mapName, service := range deployment.Subsystems.K8s.ServiceMap { - if service.Created() && mapName == deployment.Name { + for mapName, k8sService := range deployment.Subsystems.K8s.ServiceMap { + if k8sService.Created() && mapName == deployment.Name { serviceDeleted = false } } @@ -120,10 +121,10 @@ func harborCreated(deployment *deployment.Deployment) (bool, error) { return true, nil } - return harbor.Project.ID != 0 && - harbor.Robot.ID != 0 && - harbor.Repository.ID != 0 && - harbor.Webhook.ID != 0, nil + return harbor.Project.Created() && + harbor.Robot.Created() && + harbor.Repository.Created() && + harbor.Webhook.Created(), nil } func harborDeleted(deployment *deployment.Deployment) (bool, error) { @@ -148,7 +149,7 @@ func gitHubCreated(deployment *deployment.Deployment) (bool, error) { return true, nil } - return github.Webhook.ID != 0, nil + return github.Webhook.Created(), nil } func gitHubDeleted(deployment *deployment.Deployment) (bool, error) { @@ -165,9 +166,12 @@ func gitHubDeleted(deployment *deployment.Deployment) (bool, error) { func csCreated(vm *vm.VM) (bool, error) { cs := &vm.Subsystems.CS - _, hasSshRule := cs.PortForwardingRuleMap["__ssh"] + sshRule, ok := cs.PortForwardingRuleMap["__ssh"] + if !ok { + return false, nil + } - return cs.VM.ID != "" && hasSshRule, nil + return cs.VM.Created() && sshRule.Created(), nil } func csDeleted(vm *vm.VM) (bool, error) { @@ -182,6 +186,54 @@ func csDeleted(vm *vm.VM) (bool, error) { return cs.VM.ID == "", nil } +func k8sCreatedVM(vm *vm.VM) (bool, error) { + k8s := &vm.Subsystems.K8s + + for _, port := range vm.Ports { + if port.Name == "__ssh" { + continue + } + + resourceName := vm.Name + "-" + port.Name + + if service.NotCreated(k8s.GetDeployment(resourceName)) { + return false, nil + } + + if service.NotCreated(k8s.GetService(resourceName)) { + return false, nil + } + + if service.NotCreated(k8s.GetIngress(resourceName)) { + return false, nil + } + } + + if len(vm.Ports) > 1 && !k8s.Namespace.Created() { + return false, nil + } + + return true, nil +} + +func k8sDeletedVM(vm *vm.VM) (bool, error) { + k8s := &vm.Subsystems.K8s + + if len(k8s.DeploymentMap) > 0 { + return false, nil + } + + if len(k8s.ServiceMap) > 0 { + return false, nil + } + + if len(k8s.IngressMap) > 0 { + return false, nil + } + + return k8s.Namespace.ID == "", nil +} + func gpuCleared(vm *vm.VM) (bool, error) { return vm.GpuID == "", nil } diff --git a/pkg/workers/confirm/subsystems.go b/pkg/workers/confirm/subsystems.go index b4fd8576..fe90825a 100644 --- a/pkg/workers/confirm/subsystems.go +++ b/pkg/workers/confirm/subsystems.go @@ -24,6 +24,7 @@ func getDeploymentDeletedConfirmers() []func(*deployment.Deployment) (bool, erro func getVmCreatedConfirmers() []func(*vm.VM) (bool, error) { return []func(*vm.VM) (bool, error){ csCreated, + k8sCreatedVM, } } @@ -31,6 +32,7 @@ func getVmDeletedConfirmers() []func(*vm.VM) (bool, error) { return []func(*vm.VM) (bool, error){ csDeleted, gpuCleared, + k8sDeletedVM, } } diff --git a/service/vm_service/vm_service.go b/service/vm_service/vm_service.go index 526b7b8b..c4948e1e 100644 --- a/service/vm_service/vm_service.go +++ b/service/vm_service/vm_service.go @@ -42,9 +42,14 @@ func Create(id, ownerID string, vmCreate *body.VmCreate) error { return makeError(err) } - err = k8s_service.Create(id, params) - if err != nil { - return makeError(err) + // there is always at least one port: __ssh + if len(params.Ports) > 1 { + err = k8s_service.Create(id, params) + if err != nil { + return makeError(err) + } + } else { + log.Println("skipping k8s setup for vm", id, "since it has no ports") } return nil