Example #1
0
func RealAPIContext() *CommandContext {
	config, err := config.GetConfig()
	if err != nil {
		logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err)
		return nil
	}

	apiClient, err := api.NewScalewayAPI(config.ComputeAPI, config.AccountAPI, config.Organization, config.Token)
	if err != nil {
		logrus.Warnf("RealAPIContext: failed to call api.NewScalewayAPI(): %v", err)
		return nil
	}

	stdout := bytes.Buffer{}
	stderr := bytes.Buffer{}

	ctx := CommandContext{
		Streams: Streams{
			Stdin:  os.Stdin,
			Stdout: &stdout,
			Stderr: &stderr,
		},
		Env: []string{
			"HOME" + os.Getenv("HOME"),
		},
		RawArgs: []string{},
		API:     apiClient,
	}
	return &ctx
}
Example #2
0
func runPs(cmd *Command, rawArgs []string) error {
	if psHelp {
		return cmd.PrintUsage()
	}
	if len(rawArgs) != 0 {
		return cmd.PrintShortUsage()
	}

	args := commands.PsArgs{
		All:     psA,
		Latest:  psL,
		Quiet:   psQ,
		NoTrunc: psNoTrunc,
		NLast:   psN,
		Filters: make(map[string]string, 0),
	}
	if psFilters != "" {
		for _, filter := range strings.Split(psFilters, " ") {
			parts := strings.SplitN(filter, "=", 2)
			if len(parts) != 2 {
				logrus.Warnf("Invalid filter '%s', should be in the form 'key=value'", filter)
				continue
			}
			if _, ok := args.Filters[parts[0]]; ok {
				logrus.Warnf("Duplicated filter: %q", parts[0])
			} else {
				args.Filters[parts[0]] = parts[1]
			}
		}
	}
	ctx := cmd.GetContext(rawArgs)
	return commands.RunPs(ctx, args)
}
Example #3
0
// AttachToSerial tries to connect to server serial using 'term.js-cli' and fallback with a help message
func AttachToSerial(serverID string, apiToken string, attachStdin bool) error {
	termjsURL := fmt.Sprintf("https://tty.cloud.online.net?server_id=%s&type=serial&auth_token=%s", serverID, apiToken)

	args := []string{}
	if !attachStdin {
		args = append(args, "--no-stdin")
	}
	args = append(args, termjsURL)
	log.Debugf("Executing: %s %v", termjsBin, args)
	// FIXME: check if termjs-cli is installed
	spawn := exec.Command(termjsBin, args...)
	spawn.Stdout = os.Stdout
	spawn.Stdin = os.Stdin
	spawn.Stderr = os.Stderr
	err := spawn.Run()
	if err != nil {
		log.Warnf(`
You need to install '%s' from https://github.com/moul/term.js-cli

    npm install -g term.js-cli

However, you can access your serial using a web browser:

    %s

`, termjsBin, termjsURL)
		return err
	}
	return nil
}
Example #4
0
func runPatch(cmd *Command, args []string) error {
	if patchHelp {
		return cmd.PrintUsage()
	}
	if len(args) != 2 {
		return cmd.PrintShortUsage()
	}

	// Parsing FIELD=VALUE
	updateParts := strings.SplitN(args[1], "=", 2)
	if len(updateParts) != 2 {
		return cmd.PrintShortUsage()
	}
	fieldName := updateParts[0]
	newValue := updateParts[1]

	changes := 0

	ident := api.GetIdentifier(cmd.API, args[0])
	switch ident.Type {
	case api.IdentifierServer:
		currentServer, err := cmd.API.GetServer(ident.Identifier)
		if err != nil {
			log.Fatalf("Cannot get server %s: %v", ident.Identifier, err)
		}

		var payload api.ScalewayServerPatchDefinition

		switch fieldName {
		case "state_detail":
			log.Debugf("%s=%s  =>  %s=%s", fieldName, currentServer.StateDetail, fieldName, newValue)
			if currentServer.StateDetail != newValue {
				changes++
				payload.StateDetail = &newValue
			}
		case "name":
			log.Warnf("To rename a server, Use 'scw rename'")
			log.Debugf("%s=%s  =>  %s=%s", fieldName, currentServer.StateDetail, fieldName, newValue)
			if currentServer.Name != newValue {
				changes++
				payload.Name = &newValue
			}
		case "bootscript":
			log.Debugf("%s=%s  =>  %s=%s", fieldName, currentServer.Bootscript.Identifier, fieldName, newValue)
			if currentServer.Bootscript.Identifier != newValue {
				changes++
				payload.Bootscript.Identifier = newValue
			}
		case "security_group":
			log.Debugf("%s=%s  =>  %s=%s", fieldName, currentServer.SecurityGroup.Identifier, fieldName, newValue)
			if currentServer.SecurityGroup.Identifier != newValue {
				changes++
				payload.SecurityGroup.Identifier = newValue
			}
		case "tags":
			newTags := strings.Split(newValue, " ")
			log.Debugf("%s=%s  =>  %s=%s", fieldName, currentServer.Tags, fieldName, newTags)
			// fixme test equality with reflect.DeepEqual ?
			changes++
			payload.Tags = &newTags
		default:
			log.Fatalf("'_patch server %s=' not implemented", fieldName)
		}
		// FIXME: volumes, tags, dynamic_ip_required

		if changes > 0 {
			log.Debugf("updating server: %d change(s)", changes)
			err = cmd.API.PatchServer(ident.Identifier, payload)
		} else {
			log.Debugf("no changes, not updating server")
		}
		if err != nil {
			log.Fatalf("Cannot update server: %v", err)
		}
	default:
		log.Fatalf("_patch not implemented for this kind of object")
	}
	fmt.Println(ident.Identifier)

	return nil
}
Example #5
0
// RunImages is the handler for 'scw images'
func RunImages(ctx CommandContext, args ImagesArgs) error {
	wg := sync.WaitGroup{}
	chEntries := make(chan api.ScalewayImageInterface)
	var entries = []api.ScalewayImageInterface{}

	filterType := args.Filters["type"]

	// FIXME: remove log.Fatalf in routines

	if filterType == "" || filterType == "image" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			images, err := ctx.API.GetImages()
			if err != nil {
				logrus.Fatalf("unable to fetch images from the Scaleway API: %v", err)
			}
			for _, val := range *images {
				creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
				if err != nil {
					logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
				}
				chEntries <- api.ScalewayImageInterface{
					Type:         "image",
					CreationDate: creationDate,
					Identifier:   val.Identifier,
					Name:         val.Name,
					Public:       val.Public,
					Tag:          "latest",
					VirtualSize:  float64(val.RootVolume.Size),
					Organization: val.Organization,
				}
			}
		}()
	}

	if args.All || filterType != "" {
		if filterType == "" || filterType == "snapshot" {
			wg.Add(1)
			go func() {
				defer wg.Done()
				snapshots, err := ctx.API.GetSnapshots()
				if err != nil {
					logrus.Fatalf("unable to fetch snapshots from the Scaleway API: %v", err)
				}
				for _, val := range *snapshots {
					creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
					if err != nil {
						logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
					}
					chEntries <- api.ScalewayImageInterface{
						Type:         "snapshot",
						CreationDate: creationDate,
						Identifier:   val.Identifier,
						Name:         val.Name,
						Tag:          "<snapshot>",
						VirtualSize:  float64(val.Size),
						Public:       false,
						Organization: val.Organization,
					}
				}
			}()
		}

		if filterType == "" || filterType == "bootscript" {
			wg.Add(1)
			go func() {
				defer wg.Done()
				bootscripts, err := ctx.API.GetBootscripts()
				if err != nil {
					logrus.Fatalf("unable to fetch bootscripts from the Scaleway API: %v", err)
				}
				for _, val := range *bootscripts {
					chEntries <- api.ScalewayImageInterface{
						Type:       "bootscript",
						Identifier: val.Identifier,
						Name:       val.Title,
						Tag:        "<bootscript>",
						Public:     false,
					}
				}
			}()
		}

		if filterType == "" || filterType == "volume" {
			wg.Add(1)
			go func() {
				defer wg.Done()
				volumes, err := ctx.API.GetVolumes()
				if err != nil {
					logrus.Fatalf("unable to fetch volumes from the Scaleway API: %v", err)
				}
				for _, val := range *volumes {
					creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
					if err != nil {
						logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
					}
					chEntries <- api.ScalewayImageInterface{
						Type:         "volume",
						CreationDate: creationDate,
						Identifier:   val.Identifier,
						Name:         val.Name,
						Tag:          "<volume>",
						VirtualSize:  float64(val.Size),
						Public:       false,
						Organization: val.Organization,
					}
				}
			}()
		}
	}

	go func() {
		wg.Wait()
		close(chEntries)
	}()

	done := false
	for {
		select {
		case entry, ok := <-chEntries:
			if !ok {
				done = true
				break
			}
			entries = append(entries, entry)
		}
		if done {
			break
		}
	}

	for key, value := range args.Filters {
		switch key {
		case "organization", "type", "name", "public":
			continue
		default:
			logrus.Warnf("Unknown filter: '%s=%s'", key, value)
		}
	}

	w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	if !args.Quiet {
		fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE\n")
	}
	sort.Sort(api.ByCreationDate(entries))
	for _, image := range entries {

		for key, value := range args.Filters {
			switch key {
			case "type":
				if value != image.Type {
					goto skipimage
				}
			case "organization":
				switch value {
				case "me":
					value = ctx.API.Organization
				case "official-distribs":
					value = "a283af0b-d13e-42e1-a43f-855ffbf281ab"
				case "official-apps":
					value = "c3884e19-7a3e-4b69-9db8-50e7f902aafc"
				}
				if image.Organization != value {
					goto skipimage
				}
			case "name":
				if fuzzy.RankMatch(strings.ToLower(value), strings.ToLower(image.Name)) == -1 {
					goto skipimage
				}
			case "public":
				if (value == "true" && !image.Public) || (value == "false" && image.Public) {
					goto skipimage
				}
			}
		}

		if args.Quiet {
			fmt.Fprintf(ctx.Stdout, "%s\n", image.Identifier)
		} else {
			tag := image.Tag
			shortID := utils.TruncIf(image.Identifier, 8, !args.NoTrunc)
			name := utils.Wordify(image.Name)
			if !image.Public && image.Type == "image" {
				name = "user/" + name
			}
			shortName := utils.TruncIf(name, 25, !args.NoTrunc)
			var creationDate, virtualSize string
			if image.CreationDate.IsZero() {
				creationDate = "n/a"
			} else {
				creationDate = units.HumanDuration(time.Now().UTC().Sub(image.CreationDate))
			}
			if image.VirtualSize == 0 {
				virtualSize = "n/a"
			} else {
				virtualSize = units.HumanSize(image.VirtualSize)
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", shortName, tag, shortID, creationDate, virtualSize)
		}

	skipimage:
		continue
	}
	return nil
}
Example #6
0
// UnpackLayer unpack `layer` to a `dest`. The stream `layer` can be
// compressed or uncompressed.
// Returns the size in bytes of the contents of the layer.
func UnpackLayer(dest string, layer Reader) (size int64, err error) {
	tr := tar.NewReader(layer)
	trBuf := pools.BufioReader32KPool.Get(tr)
	defer pools.BufioReader32KPool.Put(trBuf)

	var dirs []*tar.Header

	aufsTempdir := ""
	aufsHardlinks := make(map[string]*tar.Header)

	// Iterate through the files in the archive.
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			return 0, err
		}

		size += hdr.Size

		// Normalize name, for safety and for a simple is-root check
		hdr.Name = filepath.Clean(hdr.Name)

		// Windows does not support filenames with colons in them. Ignore
		// these files. This is not a problem though (although it might
		// appear that it is). Let's suppose a client is running docker pull.
		// The daemon it points to is Windows. Would it make sense for the
		// client to be doing a docker pull Ubuntu for example (which has files
		// with colons in the name under /usr/share/man/man3)? No, absolutely
		// not as it would really only make sense that they were pulling a
		// Windows image. However, for development, it is necessary to be able
		// to pull Linux images which are in the repository.
		//
		// TODO Windows. Once the registry is aware of what images are Windows-
		// specific or Linux-specific, this warning should be changed to an error
		// to cater for the situation where someone does manage to upload a Linux
		// image but have it tagged as Windows inadvertently.
		if runtime.GOOS == "windows" {
			if strings.Contains(hdr.Name, ":") {
				logrus.Warnf("Windows: Ignoring %s (is this a Linux image?)", hdr.Name)
				continue
			}
		}

		// Note as these operations are platform specific, so must the slash be.
		if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
			// Not the root directory, ensure that the parent directory exists.
			// This happened in some tests where an image had a tarfile without any
			// parent directories.
			parent := filepath.Dir(hdr.Name)
			parentPath := filepath.Join(dest, parent)

			if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
				err = system.MkdirAll(parentPath, 0600)
				if err != nil {
					return 0, err
				}
			}
		}

		// Skip AUFS metadata dirs
		if strings.HasPrefix(hdr.Name, ".wh..wh.") {
			// Regular files inside /.wh..wh.plnk can be used as hardlink targets
			// We don't want this directory, but we need the files in them so that
			// such hardlinks can be resolved.
			if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg {
				basename := filepath.Base(hdr.Name)
				aufsHardlinks[basename] = hdr
				if aufsTempdir == "" {
					if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil {
						return 0, err
					}
					defer os.RemoveAll(aufsTempdir)
				}
				if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil); err != nil {
					return 0, err
				}
			}
			continue
		}
		path := filepath.Join(dest, hdr.Name)
		rel, err := filepath.Rel(dest, path)
		if err != nil {
			return 0, err
		}

		// Note as these operations are platform specific, so must the slash be.
		if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
			return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
		}
		base := filepath.Base(path)

		if strings.HasPrefix(base, ".wh.") {
			originalBase := base[len(".wh."):]
			originalPath := filepath.Join(filepath.Dir(path), originalBase)
			if err := os.RemoveAll(originalPath); err != nil {
				return 0, err
			}
		} else {
			// If path exits we almost always just want to remove and replace it.
			// The only exception is when it is a directory *and* the file from
			// the layer is also a directory. Then we want to merge them (i.e.
			// just apply the metadata from the layer).
			if fi, err := os.Lstat(path); err == nil {
				if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
					if err := os.RemoveAll(path); err != nil {
						return 0, err
					}
				}
			}

			trBuf.Reset(tr)
			srcData := io.Reader(trBuf)
			srcHdr := hdr

			// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
			// we manually retarget these into the temporary files we extracted them into
			if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") {
				linkBasename := filepath.Base(hdr.Linkname)
				srcHdr = aufsHardlinks[linkBasename]
				if srcHdr == nil {
					return 0, fmt.Errorf("Invalid aufs hardlink")
				}
				tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename))
				if err != nil {
					return 0, err
				}
				defer tmpFile.Close()
				srcData = tmpFile
			}

			if err := createTarFile(path, dest, srcHdr, srcData, true, nil); err != nil {
				return 0, err
			}

			// Directory mtimes must be handled at the end to avoid further
			// file creation in them to modify the directory mtime
			if hdr.Typeflag == tar.TypeDir {
				dirs = append(dirs, hdr)
			}
		}
	}

	for _, hdr := range dirs {
		path := filepath.Join(dest, hdr.Name)
		ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
		if err := syscall.UtimesNano(path, ts); err != nil {
			return 0, err
		}
	}

	return size, nil
}
Example #7
0
// RunPs is the handler for 'scw ps'
func RunPs(ctx CommandContext, args PsArgs) error {
	limit := args.NLast
	if args.Latest {
		limit = 1
	}

	filterState := args.Filters["state"]

	// FIXME: if filter state is defined, try to optimize the query
	all := args.All || args.NLast > 0 || args.Latest || filterState != ""
	servers, err := ctx.API.GetServers(all, limit)
	if err != nil {
		return fmt.Errorf("Unable to fetch servers from the Scaleway API: %v", err)
	}

	for key, value := range args.Filters {
		switch key {
		case "state", "name", "tags", "image", "ip":
			continue
		default:
			logrus.Warnf("Unknown filter: '%s=%s'", key, value)
		}
	}

	w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	if !args.Quiet {
		fmt.Fprintf(w, "SERVER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAME\n")
	}
	for _, server := range *servers {

		// filtering
		for key, value := range args.Filters {
			switch key {
			case "state":
				if value != server.State {
					goto skipServer
				}
			case "name":
				if fuzzy.RankMatch(strings.ToLower(value), strings.ToLower(server.Name)) == -1 {
					goto skipServer
				}
			case "tags":
				found := false
				for _, tag := range server.Tags {
					if tag == value {
						found = true
						continue
					}
				}
				if !found {
					goto skipServer
				}
			case "image":
				imageID := ctx.API.GetImageID(value, true)
				if imageID != server.Image.Identifier {
					goto skipServer
				}
			case "ip":
				if value != server.PublicAddress.IP {
					goto skipServer
				}
			}
		}

		if args.Quiet {
			fmt.Fprintf(w, "%s\n", server.Identifier)
		} else {
			shortID := utils.TruncIf(server.Identifier, 8, !args.NoTrunc)
			shortImage := utils.TruncIf(utils.Wordify(server.Image.Name), 25, !args.NoTrunc)
			shortName := utils.TruncIf(utils.Wordify(server.Name), 25, !args.NoTrunc)
			creationTime, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", server.CreationDate)
			shortCreationDate := units.HumanDuration(time.Now().UTC().Sub(creationTime))
			port := server.PublicAddress.IP
			fmt.Fprintf(w, "%s\t%s\t\t%s\t%s\t%s\t%s\n", shortID, shortImage, shortCreationDate, server.State, port, shortName)
		}
	skipServer:
		continue
	}
	return nil
}