github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/slavedriver/smallstack/impl.go (about) 1 package smallstack 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net" 7 "net/http" 8 "time" 9 10 "github.com/Cloud-Foundations/Dominator/hypervisor/client" 11 "github.com/Cloud-Foundations/Dominator/lib/constants" 12 "github.com/Cloud-Foundations/Dominator/lib/log" 13 "github.com/Cloud-Foundations/Dominator/lib/slavedriver" 14 "github.com/Cloud-Foundations/Dominator/lib/srpc" 15 "github.com/Cloud-Foundations/Dominator/lib/tags" 16 hyper_proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 17 ) 18 19 const ( 20 linklocalAddress = "169.254.169.254" 21 metadataUrl = "http://" + linklocalAddress + "/" 22 identityPath = "latest/dynamic/instance-identity/document" 23 ) 24 25 var ( 26 hypervisorAddress = fmt.Sprintf("%s:%d", 27 linklocalAddress, constants.HypervisorPortNumber) 28 myVmInfo hyper_proto.VmInfo 29 ) 30 31 func newSlaveTrader(createRequest hyper_proto.CreateVmRequest, 32 logger log.DebugLogger) (*SlaveTrader, error) { 33 trader := &SlaveTrader{ 34 createRequest: createRequest, 35 logger: logger, 36 } 37 var err error 38 trader.hypervisor, err = trader.getHypervisor() 39 if err != nil { 40 return nil, err 41 } 42 if err := readVmInfo(&myVmInfo); err != nil { 43 trader.close() 44 return nil, err 45 } 46 if trader.createRequest.Hostname == "" { 47 trader.createRequest.Hostname = myVmInfo.Hostname + "-slave" 48 } 49 if trader.createRequest.ImageName == "" { 50 trader.createRequest.ImageName = myVmInfo.ImageName 51 } 52 if trader.createRequest.MemoryInMiB < 1 { 53 trader.createRequest.MemoryInMiB = myVmInfo.MemoryInMiB 54 } 55 if trader.createRequest.MilliCPUs < 1 { 56 trader.createRequest.MilliCPUs = myVmInfo.MilliCPUs 57 } 58 if trader.createRequest.MinimumFreeBytes < 1 { 59 trader.createRequest.MinimumFreeBytes = 256 << 20 60 } 61 if trader.createRequest.RoundupPower < 1 { 62 trader.createRequest.RoundupPower = 26 63 } 64 if trader.createRequest.SubnetId == "" { 65 trader.createRequest.SubnetId = myVmInfo.SubnetId 66 } 67 if trader.createRequest.Tags["Name"] == "" { 68 trader.createRequest.Tags = tags.Tags{ 69 "Name": trader.createRequest.Hostname} 70 } 71 return trader, nil 72 } 73 74 func readVmInfo(vmInfo *hyper_proto.VmInfo) error { 75 url := metadataUrl + identityPath 76 resp, err := http.Get(url) 77 if err != nil { 78 return err 79 } 80 if resp.StatusCode != 200 { 81 return fmt.Errorf("error getting: %s: %s", url, resp.Status) 82 } 83 defer resp.Body.Close() 84 decoder := json.NewDecoder(resp.Body) 85 if err := decoder.Decode(vmInfo); err != nil { 86 return fmt.Errorf("error decoding identity document: %s", err) 87 } 88 return nil 89 } 90 91 func (trader *SlaveTrader) close() error { 92 if trader.hypervisor == nil { 93 return nil 94 } 95 err := trader.hypervisor.Close() 96 trader.hypervisor = nil 97 return err 98 } 99 100 func (trader *SlaveTrader) getHypervisor() (*srpc.Client, error) { 101 trader.mutex.Lock() 102 defer trader.mutex.Unlock() 103 if hyperClient := trader.hypervisor; hyperClient != nil { 104 if err := hyperClient.Ping(); err != nil { 105 trader.logger.Printf("error pinging hypervisor, reconnecting: %s\n", 106 err) 107 hyperClient.Close() 108 trader.hypervisor = nil 109 } else { 110 return trader.hypervisor, nil 111 } 112 } 113 hyperClient, err := srpc.DialHTTP("tcp", hypervisorAddress, time.Second*5) 114 if err != nil { 115 return nil, err 116 } 117 trader.hypervisor = hyperClient 118 return hyperClient, nil 119 } 120 121 func (trader *SlaveTrader) createSlave() (slavedriver.SlaveInfo, error) { 122 if hyperClient, err := trader.getHypervisor(); err != nil { 123 return slavedriver.SlaveInfo{}, err 124 } else { 125 var reply hyper_proto.CreateVmResponse 126 err := client.CreateVm(hyperClient, trader.createRequest, &reply, 127 trader.logger) 128 if err != nil { 129 return slavedriver.SlaveInfo{}, 130 fmt.Errorf("error creating VM: %s", err) 131 } 132 err = client.AcknowledgeVm(hyperClient, reply.IpAddress) 133 if err != nil { 134 client.DestroyVm(hyperClient, reply.IpAddress, nil) 135 return slavedriver.SlaveInfo{}, 136 fmt.Errorf("error acknowledging VM: %s", err) 137 } 138 if reply.DhcpTimedOut { 139 client.DestroyVm(hyperClient, reply.IpAddress, nil) 140 return slavedriver.SlaveInfo{}, 141 fmt.Errorf("DHCP timeout for: %s", reply.IpAddress) 142 } 143 return slavedriver.SlaveInfo{ 144 Identifier: reply.IpAddress.String(), 145 IpAddress: reply.IpAddress, 146 }, nil 147 } 148 } 149 150 func (trader *SlaveTrader) destroySlave(identifier string) error { 151 ipAddr := net.ParseIP(identifier) 152 if len(ipAddr) < 1 { 153 return fmt.Errorf("error parsing: %s", identifier) 154 } 155 if ip4 := ipAddr.To4(); ip4 != nil { 156 ipAddr = ip4 157 } 158 if hyperClient, err := trader.getHypervisor(); err != nil { 159 return err 160 } else { 161 return client.DestroyVm(hyperClient, ipAddr, nil) 162 } 163 }