// LookUpServers attempts to return identifiers matching a pattern func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) ScalewayResolverResults { c.Lock.Lock() defer c.Lock.Unlock() var res ScalewayResolverResults if acceptUUID && anonuuid.IsUUID(needle) == nil { if fields, ok := c.Servers[needle]; ok { entry := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierServer) entry.ComputeRankMatch(needle) res = append(res, entry) } } nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) var exactMatches ScalewayResolverResults for identifier, fields := range c.Servers { if fields[CacheTitle] == needle { entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer) entry.ComputeRankMatch(needle) exactMatches = append(exactMatches, entry) } if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { entry := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierServer) entry.ComputeRankMatch(needle) res = append(res, entry) } } if len(exactMatches) == 1 { return exactMatches } return removeDuplicatesResults(res) }
// RunTag is the handler for 'scw tag' func RunTag(ctx CommandContext, args TagArgs) error { snapshotID, err := ctx.API.GetSnapshotID(args.Snapshot) if err != nil { return err } snapshot, err := ctx.API.GetSnapshot(snapshotID) if err != nil { return fmt.Errorf("cannot fetch snapshot: %v", err) } bootscriptID := "" if args.Bootscript != "" { if anonuuid.IsUUID(args.Bootscript) == nil { bootscriptID = args.Bootscript } else { bootscriptID, err = ctx.API.GetBootscriptID(args.Bootscript, args.Arch) if err != nil { return err } } } image, err := ctx.API.PostImage(snapshot.Identifier, args.Name, bootscriptID, args.Arch) if err != nil { return fmt.Errorf("cannot create image: %v", err) } fmt.Fprintln(ctx.Stdout, image) return nil }
func shouldBeAnUUID(actual interface{}, expected ...interface{}) string { input := actual.(string) input = strings.TrimSpace(input) if err := anonuuid.IsUUID(input); err != nil { return fmt.Sprintf("%q should be an UUID", actual) } return "" }
// NewScalewayResolverResult returns a new ScalewayResolverResult func NewScalewayResolverResult(Identifier, Name, Arch string, Type int) ScalewayResolverResult { if err := anonuuid.IsUUID(Identifier); err != nil { log.Fatal(err) } return ScalewayResolverResult{ Identifier: Identifier, Type: Type, Name: Name, Arch: Arch, } }
// NewScalewayResolverResult returns a new ScalewayResolverResult func NewScalewayResolverResult(Identifier, Name, Arch string, Type int) (ScalewayResolverResult, error) { if err := anonuuid.IsUUID(Identifier); err != nil { return ScalewayResolverResult{}, err } return ScalewayResolverResult{ Identifier: Identifier, Type: Type, Name: Name, Arch: Arch, }, nil }
// LookUpImages attempts to return identifiers matching a pattern func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (ScalewayResolverResults, error) { c.Lock.Lock() defer c.Lock.Unlock() var res ScalewayResolverResults var exactMatches ScalewayResolverResults if acceptUUID && anonuuid.IsUUID(needle) == nil { if fields, ok := c.Images[needle]; ok { entry, err := NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], IdentifierImage) if err != nil { return ScalewayResolverResults{}, err } entry.ComputeRankMatch(needle) res = append(res, entry) } } needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") // FIXME: if 'user/' is in needle, only watch for a user image nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) for identifier, fields := range c.Images { if fields[CacheTitle] == needle { entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage) if err != nil { return ScalewayResolverResults{}, err } entry.ComputeRankMatch(needle) exactMatches = append(exactMatches, entry) } if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage) if err != nil { return ScalewayResolverResults{}, err } entry.ComputeRankMatch(needle) res = append(res, entry) } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { entry, err := NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], IdentifierImage) if err != nil { return ScalewayResolverResults{}, err } entry.ComputeRankMatch(needle) res = append(res, entry) } } if len(exactMatches) == 1 { return exactMatches, nil } return removeDuplicatesResults(res), nil }
// CreateServer creates a server using API based on typical server fields func CreateServer(api *ScalewayAPI, c *ConfigCreateServer) (string, error) { if c.CommercialType == "" { c.CommercialType = os.Getenv("SCW_COMMERCIAL_TYPE") if c.CommercialType == "" { c.CommercialType = "C1" } } if c.Name == "" { c.Name = strings.Replace(namesgenerator.GetRandomName(0), "_", "-", -1) } var server ScalewayServerDefinition server.CommercialType = c.CommercialType server.Volumes = make(map[string]string) server.DynamicIPRequired = &c.DynamicIPRequired if c.IP != "" { if anonuuid.IsUUID(c.IP) == nil { server.PublicIP = c.IP } else { ips, err := api.GetIPS() if err != nil { return "", err } for _, ip := range ips.IPS { if ip.Address == c.IP { server.PublicIP = ip.ID break } } if server.PublicIP == "" { return "", fmt.Errorf("IP address %v not found", c.IP) } } } server.Tags = []string{} if c.Env != "" { server.Tags = strings.Split(c.Env, " ") } if c.AdditionalVolumes != "" { volumes := strings.Split(c.AdditionalVolumes, " ") for i := range volumes { volumeID, err := CreateVolumeFromHumanSize(api, volumes[i]) if err != nil { return "", err } volumeIDx := fmt.Sprintf("%d", i+1) server.Volumes[volumeIDx] = *volumeID } } // FIXME build images only on ARM ? imageIdentifier := &ScalewayImageIdentifier{ Arch: "arm", Region: "fr-1", } server.Name = c.Name inheritingVolume := false _, err := humanize.ParseBytes(c.ImageName) if err == nil { // Create a new root volume volumeID, err := CreateVolumeFromHumanSize(api, c.ImageName) if err != nil { return "", err } server.Volumes["0"] = *volumeID } else { // Use an existing image // FIXME: handle snapshots inheritingVolume = true imageIdentifier, err = api.GetImageID(c.ImageName) if err != nil { return "", err } if imageIdentifier.Identifier != "" { server.Image = &imageIdentifier.Identifier } else { snapshotID, err := api.GetSnapshotID(c.ImageName) if err != nil { return "", err } snapshot, err := api.GetSnapshot(snapshotID) if err != nil { return "", err } if snapshot.BaseVolume.Identifier == "" { return "", fmt.Errorf("snapshot %v does not have base volume", snapshot.Name) } server.Volumes["0"] = snapshot.BaseVolume.Identifier } } if c.Bootscript != "" { bootscript, err := api.GetBootscriptID(c.Bootscript, imageIdentifier.Arch) if err != nil { return "", err } server.Bootscript = &bootscript } serverID, err := api.PostServer(server) if err != nil { return "", err } // For inherited volumes, we prefix the name with server hostname if inheritingVolume { createdServer, err := api.GetServer(serverID) if err != nil { return "", err } currentVolume := createdServer.Volumes["0"] var volumePayload ScalewayVolumePutDefinition newName := fmt.Sprintf("%s-%s", createdServer.Hostname, currentVolume.Name) volumePayload.Name = &newName volumePayload.CreationDate = ¤tVolume.CreationDate volumePayload.Organization = ¤tVolume.Organization volumePayload.Server.Identifier = ¤tVolume.Server.Identifier volumePayload.Server.Name = ¤tVolume.Server.Name volumePayload.Identifier = ¤tVolume.Identifier volumePayload.Size = ¤tVolume.Size volumePayload.ModificationDate = ¤tVolume.ModificationDate volumePayload.ExportURI = ¤tVolume.ExportURI volumePayload.VolumeType = ¤tVolume.VolumeType err = api.PutVolume(currentVolume.Identifier, volumePayload) if err != nil { return "", err } } return serverID, nil }
// CreateServer creates a server using API based on typical server fields func CreateServer(api *ScalewayAPI, c *ConfigCreateServer) (string, error) { commercialType := os.Getenv("SCW_COMMERCIAL_TYPE") if commercialType == "" { commercialType = c.CommercialType } if len(commercialType) < 2 { return "", errors.New("Invalid commercial type") } if c.Name == "" { c.Name = strings.Replace(namesgenerator.GetRandomName(0), "_", "-", -1) } var server ScalewayServerDefinition server.CommercialType = commercialType server.Volumes = make(map[string]string) server.DynamicIPRequired = &c.DynamicIPRequired server.EnableIPV6 = c.EnableIPV6 if commercialType == "" { return "", errors.New("You need to specify a commercial-type") } if c.IP != "" { if anonuuid.IsUUID(c.IP) == nil { server.PublicIP = c.IP } else { ips, err := api.GetIPS() if err != nil { return "", err } for _, ip := range ips.IPS { if ip.Address == c.IP { server.PublicIP = ip.ID break } } if server.PublicIP == "" { return "", fmt.Errorf("IP address %v not found", c.IP) } } } server.Tags = []string{} if c.Env != "" { server.Tags = strings.Split(c.Env, " ") } switch c.CommercialType { case "VC1M": if c.AdditionalVolumes == "" { c.AdditionalVolumes = "50G" log.Debugf("This server needs a least 50G") } case "VC1L": if c.AdditionalVolumes == "" { c.AdditionalVolumes = "150G" log.Debugf("This server needs a least 150G") } } if c.AdditionalVolumes != "" { volumes := strings.Split(c.AdditionalVolumes, " ") for i := range volumes { volumeID, err := CreateVolumeFromHumanSize(api, volumes[i]) if err != nil { return "", err } volumeIDx := fmt.Sprintf("%d", i+1) server.Volumes[volumeIDx] = *volumeID } } arch := os.Getenv("SCW_TARGET_ARCH") if arch == "" { server.CommercialType = strings.ToUpper(server.CommercialType) switch server.CommercialType[:2] { case "C1": arch = "arm" case "C2", "VC": arch = "x86_64" default: return "", fmt.Errorf("%s wrong commercial type", server.CommercialType) } } region := os.Getenv("SCW_TARGET_REGION") if region == "" { region = "fr-1" } imageIdentifier := &ScalewayImageIdentifier{ Arch: arch, Region: region, } server.Name = c.Name inheritingVolume := false _, err := humanize.ParseBytes(c.ImageName) if err == nil { // Create a new root volume volumeID, errCreateVol := CreateVolumeFromHumanSize(api, c.ImageName) if errCreateVol != nil { return "", errCreateVol } server.Volumes["0"] = *volumeID } else { // Use an existing image inheritingVolume = true if anonuuid.IsUUID(c.ImageName) == nil { server.Image = &c.ImageName } else { imageIdentifier, err = api.GetImageID(c.ImageName, arch) if err != nil { return "", err } if imageIdentifier.Identifier != "" { server.Image = &imageIdentifier.Identifier } else { snapshotID, errGetSnapID := api.GetSnapshotID(c.ImageName) if errGetSnapID != nil { return "", errGetSnapID } snapshot, errGetSnap := api.GetSnapshot(snapshotID) if errGetSnap != nil { return "", errGetSnap } if snapshot.BaseVolume.Identifier == "" { return "", fmt.Errorf("snapshot %v does not have base volume", snapshot.Name) } server.Volumes["0"] = snapshot.BaseVolume.Identifier } } } if c.Bootscript != "" { bootscript := "" if anonuuid.IsUUID(c.Bootscript) == nil { bootscript = c.Bootscript } else { var errGetBootScript error bootscript, errGetBootScript = api.GetBootscriptID(c.Bootscript, imageIdentifier.Arch) if errGetBootScript != nil { return "", errGetBootScript } } server.Bootscript = &bootscript } serverID, err := api.PostServer(server) if err != nil { return "", err } // For inherited volumes, we prefix the name with server hostname if inheritingVolume { createdServer, err := api.GetServer(serverID) if err != nil { return "", err } currentVolume := createdServer.Volumes["0"] size := uint64(currentVolume.Size.(float64)) var volumePayload ScalewayVolumePutDefinition newName := fmt.Sprintf("%s-%s", createdServer.Hostname, currentVolume.Name) volumePayload.Name = &newName volumePayload.CreationDate = ¤tVolume.CreationDate volumePayload.Organization = ¤tVolume.Organization volumePayload.Server.Identifier = ¤tVolume.Server.Identifier volumePayload.Server.Name = ¤tVolume.Server.Name volumePayload.Identifier = ¤tVolume.Identifier volumePayload.Size = &size volumePayload.ModificationDate = ¤tVolume.ModificationDate volumePayload.ExportURI = ¤tVolume.ExportURI volumePayload.VolumeType = ¤tVolume.VolumeType err = api.PutVolume(currentVolume.Identifier, volumePayload) if err != nil { return "", err } } return serverID, nil }