// Create a single server func (vp *vultrProvider) createServer(options providers.CreateInstanceOptions) (string, error) { // Find SSH key ID var sshid string if len(options.SSHKeyNames) > 0 { var err error sshid, err = vp.findSSHKeyID(options.SSHKeyNames[0]) if err != nil { return "", maskAny(err) } } // Fetch SSH keys sshKeys, err := providers.FetchSSHKeys(options.SSHKeyGithubAccount) if err != nil { return "", maskAny(err) } // Create cloud-config // user-data ccOpts := options.NewCloudConfigOptions() ccOpts.PrivateIPv4 = "$private_ipv4" ccOpts.SshKeys = sshKeys userData, err := templates.Render(cloudConfigTemplate, ccOpts) if err != nil { return "", maskAny(err) } name := options.InstanceName opts := &lib.ServerOptions{ IPV6: true, PrivateNetworking: true, SSHKey: sshid, UserData: userData, } regionID, err := strconv.Atoi(options.RegionID) if err != nil { return "", maskAny(err) } planID, err := strconv.Atoi(options.TypeID) if err != nil { return "", maskAny(err) } osID, err := strconv.Atoi(options.ImageID) if err != nil { return "", maskAny(err) } server, err := vp.client.CreateServer(name, regionID, planID, osID, opts) if err != nil { vp.Logger.Debugf("CreateServer failed: %#v", err) return "", maskAny(err) } vp.Logger.Infof("Created server %s %s\n", server.ID, server.Name) return server.ID, nil }
// bootstrapServer copies etcd & fleet into the instances and runs the scaleway bootstrap script. // It then reboots the instances and waits until it is active again. func (vp *scalewayProvider) bootstrapServer(instance providers.ClusterInstance, options providers.CreateInstanceOptions, machineID string) error { if err := vp.copyEtcd(instance); err != nil { vp.Logger.Errorf("copy etcd failed: %#v", err) return maskAny(err) } if err := vp.copyFleet(instance); err != nil { vp.Logger.Errorf("copy fleet failed: %#v", err) return maskAny(err) } // Bootstrap bootstrapOptions := struct { ScalewayProviderConfig providers.CreateInstanceOptions MachineID string }{ ScalewayProviderConfig: vp.ScalewayProviderConfig, CreateInstanceOptions: options, MachineID: machineID, } bootstrap, err := templates.Render(bootstrapTemplate, bootstrapOptions) if err != nil { return maskAny(err) } vp.Logger.Infof("Running bootstrap on %s. This may take a while...", instance.Name) if err := instance.RunScript(vp.Logger, bootstrap, "/root/pulcy-bootstrap.sh"); err != nil { // Failed expected because of a reboot vp.Logger.Debugf("bootstrap failed (expected): %#v", err) } vp.Logger.Infof("Done running bootstrap on %s, rebooting...", instance.Name) if err := vp.client.PostServerAction(instance.ID, "reboot"); err != nil { vp.Logger.Errorf("reboot failed: %#v", err) return maskAny(err) } time.Sleep(time.Second * 5) if _, err := vp.waitUntilServerActive(instance.ID, false); err != nil { return maskAny(err) } vp.Logger.Infof("Created server %s %s\n", instance.ID, instance.Name) return nil }
// createAndStartServer creates a new server and starts it. // It then waits until the instance is active. func (vp *scalewayProvider) createAndStartServer(options providers.CreateInstanceOptions) (providers.ClusterInstance, error) { zeroInstance := providers.ClusterInstance{} // Validate input if options.TincIpv4 == "" { return zeroInstance, maskAny(fmt.Errorf("TincIpv4 is empty")) } // Fetch SSH keys sshKeys, err := providers.FetchSSHKeys(options.SSHKeyGithubAccount) if err != nil { return zeroInstance, maskAny(err) } // Create cloud-config // user-data ccOpts := options.NewCloudConfigOptions() ccOpts.PrivateIPv4 = "$private_ipv4" ccOpts.SshKeys = sshKeys _ /*userData*/, err = templates.Render(cloudConfigTemplate, ccOpts) if err != nil { return zeroInstance, maskAny(err) } var arch string switch options.TypeID[:2] { case "C1": arch = "arm" case "C2", "VC": arch = "x86_64" } // Find image imageID, err := vp.getImageID(options.ImageID, arch) if err != nil { vp.Logger.Errorf("getImageID failed: %#v", err) return zeroInstance, maskAny(err) } name := options.InstanceName image := &imageID dynamicIPRequired := !vp.NoIPv4 //bootscript := "" volID := "" /*if options.TypeID != commercialTypeVC1 { volDef := api.ScalewayVolumeDefinition{ Name: vp.volumeName(name), Size: volumeSize, Type: volumeType, Organization: vp.organization, } volID, err = vp.client.PostVolume(volDef) if err != nil { vp.Logger.Errorf("PostVolume failed: %#v", err) return zeroInstance, maskAny(err) } }*/ publicIPIdentifier := "" if options.RoleLoadBalancer && vp.ReserveLoadBalancerIP { ip, err := vp.getFreeIP() if err != nil { return zeroInstance, maskAny(err) } publicIPIdentifier = ip.ID } opts := api.ScalewayServerDefinition{ Name: name, Image: image, Volumes: map[string]string{}, DynamicIPRequired: &dynamicIPRequired, //Bootscript: &bootscript, Tags: []string{ options.ClusterInfo.ID, options.TincIpv4, }, Organization: vp.Organization, CommercialType: options.TypeID, PublicIP: publicIPIdentifier, EnableIPV6: vp.EnableIPV6, } if volID != "" { opts.Volumes["0"] = volID } vp.Logger.Debugf("Creating server %s: %#v\n", name, opts) id, err := vp.client.PostServer(opts) if err != nil { vp.Logger.Errorf("PostServer failed: %#v", err) // Delete volume if volID != "" { if err := vp.client.DeleteVolume(volID); err != nil { vp.Logger.Errorf("DeleteVolume failed: %#v", err) } } return zeroInstance, maskAny(err) } // Start server if err := vp.client.PostServerAction(id, "poweron"); err != nil { vp.Logger.Errorf("poweron failed: %#v", err) return zeroInstance, maskAny(err) } // Wait until server starts server, err := vp.waitUntilServerActive(id, true) if err != nil { return zeroInstance, maskAny(err) } // Download & copy fleet,etcd instance := vp.clusterInstance(server, true) return instance, nil }
// Create an entire cluster func (vp *vagrantProvider) CreateCluster(log *logging.Logger, options providers.CreateClusterOptions, dnsProvider providers.DnsProvider) error { // Ensure folder exists if err := os.MkdirAll(vp.folder, fileMode|os.ModeDir); err != nil { return maskAny(err) } if _, err := os.Stat(filepath.Join(vp.folder, ".vagrant")); err == nil { return maskAny(fmt.Errorf("Vagrant in %s already exists", vp.folder)) } parts := strings.Split(options.ImageID, "-") if len(parts) != 2 || parts[0] != "coreos" { return maskAny(fmt.Errorf("Invalid image ID, expected 'coreos-alpha|beta|stable', got '%s'", options.ImageID)) } updateChannel := parts[1] vopts := struct { InstanceCount int UpdateChannel string }{ InstanceCount: options.InstanceCount, UpdateChannel: updateChannel, } vp.instanceCount = options.InstanceCount // Vagrantfile content, err := templates.Render(vagrantFileTemplate, vopts) if err != nil { return maskAny(err) } if err := ioutil.WriteFile(filepath.Join(vp.folder, vagrantFileName), []byte(content), fileMode); err != nil { return maskAny(err) } // config.rb content, err = templates.Render(configTemplate, vopts) if err != nil { return maskAny(err) } if err := ioutil.WriteFile(filepath.Join(vp.folder, configFileName), []byte(content), fileMode); err != nil { return maskAny(err) } // Fetch SSH keys sshKeys, err := providers.FetchSSHKeys(options.SSHKeyGithubAccount) if err != nil { return maskAny(err) } // Fetch vagrant insecure private key insecureKey, err := fetchVagrantInsecureSSHKey() if err != nil { return maskAny(err) } sshKeys = append(sshKeys, insecureKey) // user-data isCore := true isLB := true instanceOptions, err := options.NewCreateInstanceOptions(isCore, isLB, 0) if err != nil { return maskAny(err) } opts := instanceOptions.NewCloudConfigOptions() opts.PrivateIPv4 = "$private_ipv4" opts.SshKeys = sshKeys content, err = templates.Render(cloudConfigTemplate, opts) if err != nil { return maskAny(err) } if err := ioutil.WriteFile(filepath.Join(vp.folder, userDataFileName), []byte(content), fileMode); err != nil { return maskAny(err) } // Start cmd := exec.Command("vagrant", "up") cmd.Dir = vp.folder cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin if err := cmd.Run(); err != nil { return maskAny(err) } // Run initial setup instances, err := vp.GetInstances(providers.ClusterInfo{}) if err != nil { return maskAny(err) } clusterMembers, err := instances.AsClusterMemberList(log, nil) if err != nil { return maskAny(err) } for index, instance := range instances { iso := providers.InitialSetupOptions{ ClusterMembers: clusterMembers, FleetMetadata: instanceOptions.CreateFleetMetadata(index), } if err := instance.InitialSetup(log, instanceOptions, iso, vp); err != nil { return maskAny(err) } } return nil }
func (dp *doProvider) CreateInstance(log *logging.Logger, options providers.CreateInstanceOptions, dnsProvider providers.DnsProvider) (providers.ClusterInstance, error) { client := NewDOClient(dp.token) keys := []godo.DropletCreateSSHKey{} listedKeys, err := KeyList(client) if err != nil { return providers.ClusterInstance{}, maskAny(err) } for _, key := range options.SSHKeyNames { k := findKeyID(key, listedKeys) if k == nil { return providers.ClusterInstance{}, maskAny(errors.New("Key not found")) } keys = append(keys, godo.DropletCreateSSHKey{ID: k.ID}) } opts := options.NewCloudConfigOptions() opts.PrivateIPv4 = "$private_ipv4" cloudConfig, err := templates.Render(cloudConfigTemplate, opts) if err != nil { return providers.ClusterInstance{}, maskAny(err) } request := &godo.DropletCreateRequest{ Name: options.InstanceName, Region: options.RegionID, Size: options.TypeID, Image: godo.DropletCreateImage{Slug: options.ImageID}, SSHKeys: keys, Backups: false, IPv6: true, PrivateNetworking: true, UserData: cloudConfig, } // Create droplet dp.Logger.Infof("Creating droplet: %s, %s, %s", request.Region, request.Size, options.ImageID) dp.Logger.Debugf(cloudConfig) createDroplet, _, err := client.Droplets.Create(request) if err != nil { return providers.ClusterInstance{}, maskAny(err) } // Wait for active dp.Logger.Infof("Waiting for droplet '%s'", createDroplet.Name) droplet, err := dp.waitUntilDropletActive(createDroplet.ID) if err != nil { return providers.ClusterInstance{}, maskAny(err) } privateIpv4 := getIpv4(*droplet, "private") publicIpv4 := getIpv4(*droplet, "public") publicIpv6 := getIpv6(*droplet, "public") if err := providers.RegisterInstance(dp.Logger, dnsProvider, options, createDroplet.Name, options.RegisterInstance, options.RoleLoadBalancer, options.RoleLoadBalancer, publicIpv4, publicIpv6, privateIpv4); err != nil { return providers.ClusterInstance{}, maskAny(err) } dp.Logger.Infof("Droplet '%s' is ready", createDroplet.Name) return dp.clusterInstance(*droplet), nil }