// Calling in parallel is ok func (gc *gceCluster) NewMachine(cloudConfig string) (Machine, error) { cconfig, err := config.NewCloudConfig(cloudConfig) if err != nil { return nil, err } if err = gc.sshAgent.UpdateConfig(cconfig); err != nil { return nil, err } cloudConfig = cconfig.String() // Create gce VM and wait for creation to succeed. gm, err := GCECreateVM(gc.api, gc.conf, cloudConfig) if err != nil { return nil, err } gm.gc = gc err = sshCheck(gm) if err != nil { gm.Destroy() return nil, err } gc.mu.Lock() gc.machines[gm.ID()] = gm gc.mu.Unlock() return Machine(gm), nil }
// NewConf parses userdata and returns a new Conf. It returns an error if the // userdata can't be parsed as a coreos-cloudinit or ignition configuration. func NewConf(userdata string) (*Conf, error) { c := &Conf{} ignc, err := ign.Parse([]byte(userdata)) switch err { case ign.ErrEmpty: // empty, noop // XXX(mischief): i would use ignition as the default config, // but there's no way to load it in qemu yet. c.cloudconfig = &defConfig case ign.ErrCloudConfig: // fall back to cloud-config c.cloudconfig, err = cci.NewCloudConfig(userdata) if err != nil { return nil, err } default: // some other error (invalid json, script) return nil, err case nil: c.ignition = &ignc } return c, nil }
func GCECreateVM(api *compute.Service, opts *GCEOptions, userdata string) (*gceMachine, error) { if userdata != "" { _, err := config.NewCloudConfig(userdata) if err != nil { return nil, err } } // generate name name, err := newName(opts) if err != nil { return nil, fmt.Errorf("Failed allocating unique name for vm: %v\n", err) } instance, err := gceMakeInstance(opts, userdata, name) if err != nil { return nil, err } // request instance op, err := api.Instances.Insert(opts.Project, opts.Zone, instance).Do() if err != nil { return nil, fmt.Errorf("Failed to create new VM: %v\n", err) } fmt.Fprintf(os.Stderr, "Instance %v requested\n", name) fmt.Fprintf(os.Stderr, "Waiting for creation to finish...\n") // wait for creation to finish err = gceWaitVM(api, opts.Project, opts.Zone, op.Name) if err != nil { return nil, err } inst, err := api.Instances.Get(opts.Project, opts.Zone, name).Do() if err != nil { return nil, fmt.Errorf("Error getting instance %s details after creation: %v", name, err) } intIP, extIP := instanceIPs(inst) gm := &gceMachine{ name: name, extIP: extIP, intIP: intIP, } return gm, nil }
func (ac *awsCluster) NewMachine(userdata string) (Machine, error) { cloudConfig, err := config.NewCloudConfig(userdata) if err != nil { return nil, err } if err = ac.agent.UpdateConfig(cloudConfig); err != nil { return nil, err } if cloudConfig.Hostname == "" { id := make([]byte, 4) _, _ = rand.Read(id) cloudConfig.Hostname = fmt.Sprintf("%x", id) } ud := base64.StdEncoding.EncodeToString([]byte(cloudConfig.String())) cnt := int64(1) inst := ec2.RunInstancesInput{ ImageId: &ac.conf.AMI, MinCount: &cnt, MaxCount: &cnt, KeyName: &ac.conf.KeyName, // this is only useful if you wish to ssh in for debugging InstanceType: &ac.conf.InstanceType, SecurityGroups: []*string{&ac.conf.SecurityGroup}, UserData: &ud, } resp, err := ac.api.RunInstances(&inst) if err != nil { return nil, err } ids := []*string{resp.Instances[0].InstanceId} if err := waitForAWSInstances(ac.api, ids, 5*time.Minute); err != nil { return nil, err } getinst := &ec2.DescribeInstancesInput{ InstanceIds: ids, } insts, err := ac.api.DescribeInstances(getinst) if err != nil { return nil, err } mach := &awsMachine{ cluster: ac, mach: insts.Reservations[0].Instances[0], } // Allow a few authentication failures in case setup is slow. sshchecker := func() error { mach.sshClient, err = mach.cluster.agent.NewClient(mach.IP()) if err != nil { return err } return nil } if err := util.Retry(sshRetries, sshTimeout, sshchecker); err != nil { mach.Destroy() return nil, err } ac.addMach(mach) return mach, nil }
func (qc *qemuCluster) NewMachine(cfg string) (Machine, error) { id := uuid.NewV4() // hacky solution for cloud config ip substitution // NOTE: escaping is not supported qc.mu.Lock() netif := qc.Dnsmasq.GetInterface("br0") ip := strings.Split(netif.DHCPv4[0].String(), "/")[0] cfg = strings.Replace(cfg, "$public_ipv4", ip, -1) cfg = strings.Replace(cfg, "$private_ipv4", ip, -1) cloudConfig, err := config.NewCloudConfig(cfg) if err != nil { qc.mu.Unlock() return nil, err } if err = qc.SSHAgent.UpdateConfig(cloudConfig); err != nil { qc.mu.Unlock() return nil, err } if cloudConfig.Hostname == "" { cloudConfig.Hostname = id.String()[:8] } qc.mu.Unlock() configDrive, err := local.NewConfigDrive(cloudConfig) if err != nil { return nil, err } qm := &qemuMachine{ qc: qc, id: id.String(), configDrive: configDrive, netif: netif, } disk, err := setupDisk(qc.conf.DiskImage) if err != nil { return nil, err } defer disk.Close() qc.mu.Lock() tap, err := qc.NewTap("br0") if err != nil { qc.mu.Unlock() return nil, err } defer tap.Close() qmMac := qm.netif.HardwareAddr.String() qmCfg := qm.configDrive.Directory qm.qemu = qm.qc.NewCommand( "qemu-system-x86_64", "-machine", "accel=kvm", "-cpu", "host", "-smp", "2", "-m", "1024", "-uuid", qm.id, "-display", "none", "-add-fd", "fd=3,set=1", "-drive", "file=/dev/fdset/1,media=disk,if=virtio,format=raw", "-netdev", "tap,id=tap,fd=4", "-device", "virtio-net,netdev=tap,mac="+qmMac, "-fsdev", "local,id=cfg,security_model=none,readonly,path="+qmCfg, "-device", "virtio-9p-pci,fsdev=cfg,mount_tag=config-2") qc.mu.Unlock() cmd := qm.qemu.(*local.NsCmd) cmd.Stderr = os.Stderr cmd.ExtraFiles = append(cmd.ExtraFiles, disk) // fd=3 cmd.ExtraFiles = append(cmd.ExtraFiles, tap.File) // fd=4 if err = qm.qemu.Start(); err != nil { return nil, err } // Allow a few authentication failures in case setup is slow. sshchecker := func() error { qm.qc.mu.Lock() defer qm.qc.mu.Unlock() qm.sshClient, err = qm.qc.SSHAgent.NewClient(qm.IP()) if err != nil { return err } return nil } if err := util.Retry(sshRetries, sshTimeout, sshchecker); err != nil { return nil, err } if err != nil { qm.Destroy() return nil, err } out, err := qm.SSH("grep ^ID= /etc/os-release") if err != nil { qm.Destroy() return nil, err } if !bytes.Equal(out, []byte("ID=coreos")) { qm.Destroy() return nil, fmt.Errorf("Unexpected SSH output: %s", out) } qc.mu.Lock() qc.machines[qm.ID()] = qm qc.mu.Unlock() return Machine(qm), nil }