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  }