mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			283 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"flag"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
	"text/tabwriter"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/containers/image/v4/types"
 | 
						|
	"github.com/containers/libpod/libpod"
 | 
						|
	image2 "github.com/containers/libpod/libpod/image"
 | 
						|
	cc "github.com/containers/libpod/pkg/spec"
 | 
						|
	"github.com/containers/libpod/pkg/util"
 | 
						|
	"github.com/containers/storage/pkg/reexec"
 | 
						|
	"github.com/cri-o/ocicni/pkg/ocicni"
 | 
						|
	"github.com/pkg/profile"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	defaultTestImage = "docker.io/library/alpine:latest"
 | 
						|
	defaultRunCount  = 50
 | 
						|
)
 | 
						|
 | 
						|
var helpMessage = `
 | 
						|
-count int
 | 
						|
	count of loop counter for test (default 50)
 | 
						|
-image string
 | 
						|
	image-name to be used for test (default "docker.io/library/alpine:latest")
 | 
						|
-log string
 | 
						|
	log level (info|debug|warn|error) (default "error")
 | 
						|
 | 
						|
`
 | 
						|
 | 
						|
func main() {
 | 
						|
	if reexec.Init() {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	ctx := context.Background()
 | 
						|
	imageName := ""
 | 
						|
 | 
						|
	testImageName := flag.String("image", defaultTestImage, "image-name to be used for test")
 | 
						|
	testRunCount := flag.Int("count", defaultRunCount, "count of loop counter for test")
 | 
						|
	logLevel := flag.String("log", "error", "log level (info|debug|warn|error)")
 | 
						|
 | 
						|
	flag.Usage = func() {
 | 
						|
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
 | 
						|
		fmt.Fprintf(os.Stderr, "%s \n", helpMessage)
 | 
						|
	}
 | 
						|
 | 
						|
	flag.Parse()
 | 
						|
 | 
						|
	switch strings.ToLower(*logLevel) {
 | 
						|
	case "error":
 | 
						|
		logrus.SetLevel(logrus.ErrorLevel)
 | 
						|
	case "warn":
 | 
						|
		logrus.SetLevel(logrus.WarnLevel)
 | 
						|
	case "info":
 | 
						|
		logrus.SetLevel(logrus.InfoLevel)
 | 
						|
	case "debug":
 | 
						|
		logrus.SetLevel(logrus.DebugLevel)
 | 
						|
	default:
 | 
						|
		logrus.Fatalf("invalid option : %s ", *logLevel)
 | 
						|
	}
 | 
						|
 | 
						|
	opts := defaultRuntimeOptions()
 | 
						|
	client, err := libpod.NewRuntime(opts...)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Fatal(err)
 | 
						|
	}
 | 
						|
	defer client.Shutdown(false)
 | 
						|
 | 
						|
	// Print Runtime & System Information.
 | 
						|
	err = printSystemInfo(client)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	imageRuntime := client.ImageRuntime()
 | 
						|
	if imageRuntime == nil {
 | 
						|
		logrus.Fatal("ImageRuntime is null")
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Printf("preparing test environment...\n")
 | 
						|
	//Prepare for test.
 | 
						|
	testImage, err := imageRuntime.NewFromLocal(*testImageName)
 | 
						|
	if err != nil {
 | 
						|
		// Download the image from remote registry.
 | 
						|
		writer := os.Stderr
 | 
						|
		registryCreds := &types.DockerAuthConfig{
 | 
						|
			Username: "",
 | 
						|
			Password: "",
 | 
						|
		}
 | 
						|
		dockerRegistryOptions := image2.DockerRegistryOptions{
 | 
						|
			DockerRegistryCreds:         registryCreds,
 | 
						|
			DockerCertPath:              "",
 | 
						|
			DockerInsecureSkipTLSVerify: types.OptionalBoolFalse,
 | 
						|
		}
 | 
						|
		fmt.Printf("image %s not found locally, fetching from remote registry..\n", *testImageName)
 | 
						|
 | 
						|
		testImage, err = client.ImageRuntime().New(ctx, *testImageName, "", "", writer, &dockerRegistryOptions, image2.SigningOptions{}, nil, util.PullImageMissing)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
		fmt.Printf("image downloaded successfully\n\n")
 | 
						|
	}
 | 
						|
 | 
						|
	names := testImage.Names()
 | 
						|
	if len(names) > 0 {
 | 
						|
		imageName = names[0]
 | 
						|
	} else {
 | 
						|
		imageName = testImage.ID()
 | 
						|
	}
 | 
						|
 | 
						|
	idmappings, err := util.ParseIDMapping(nil, nil, "", "")
 | 
						|
	if err != nil {
 | 
						|
		logrus.Fatal(err)
 | 
						|
	}
 | 
						|
	config := &cc.CreateConfig{
 | 
						|
		Tty:        true,
 | 
						|
		Image:      imageName,
 | 
						|
		ImageID:    testImage.ID(),
 | 
						|
		IDMappings: idmappings,
 | 
						|
		Command:    []string{"/bin/sh"},
 | 
						|
		WorkDir:    "/",
 | 
						|
		NetMode:    "bridge",
 | 
						|
		Network:    "bridge",
 | 
						|
	}
 | 
						|
 | 
						|
	// Enable CPU Profile
 | 
						|
	defer profile.Start().Stop()
 | 
						|
 | 
						|
	data, err := runSingleThreadedStressTest(ctx, client, imageName, testImage.ID(), config, *testRunCount)
 | 
						|
	if err != nil {
 | 
						|
		logrus.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	data.printProfiledData((float64)(*testRunCount))
 | 
						|
}
 | 
						|
 | 
						|
func defaultRuntimeOptions() []libpod.RuntimeOption {
 | 
						|
	options := []libpod.RuntimeOption{}
 | 
						|
	return options
 | 
						|
	/*
 | 
						|
		//TODO: Shall we test in clean environment?
 | 
						|
		sOpts := storage.StoreOptions{
 | 
						|
			GraphDriverName: "overlay",
 | 
						|
			RunRoot:         "/var/run/containers/storage",
 | 
						|
			GraphRoot:       "/var/lib/containers/storage",
 | 
						|
		}
 | 
						|
 | 
						|
		storageOpts := libpod.WithStorageConfig(sOpts)
 | 
						|
		options = append(options, storageOpts)
 | 
						|
		return options
 | 
						|
	*/
 | 
						|
}
 | 
						|
 | 
						|
func printSystemInfo(client *libpod.Runtime) error {
 | 
						|
	OCIRuntimeInfo, err := client.GetOCIRuntimeVersion()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	connmanInfo, err := client.GetConmonVersion()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	fmt.Printf("%s\n%s\n\n", OCIRuntimeInfo, connmanInfo)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func runSingleThreadedStressTest(ctx context.Context, client *libpod.Runtime, imageName string, imageID string, config *cc.CreateConfig, testCount int) (*profileData, error) {
 | 
						|
	data := new(profileData)
 | 
						|
	fmt.Printf("Test Round: ")
 | 
						|
	for i := 0; i < testCount; i++ {
 | 
						|
		fmt.Printf("%d ", i)
 | 
						|
 | 
						|
		runtimeSpec, err := cc.CreateConfigToOCISpec(config)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		//Create Container
 | 
						|
		networks := make([]string, 0)
 | 
						|
		netmode := "bridge"
 | 
						|
		createStartTime := time.Now()
 | 
						|
		ctr, err := client.NewContainer(ctx,
 | 
						|
			runtimeSpec,
 | 
						|
			libpod.WithRootFSFromImage(imageID, imageName, false),
 | 
						|
			libpod.WithNetNS([]ocicni.PortMapping{}, false, netmode, networks),
 | 
						|
		)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		createTotalTime := time.Now().Sub(createStartTime)
 | 
						|
 | 
						|
		// Start container
 | 
						|
		startStartTime := time.Now()
 | 
						|
		err = ctr.Start(ctx, false)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		startTotalTime := time.Now().Sub(startStartTime)
 | 
						|
 | 
						|
		//Stop Container
 | 
						|
		stopStartTime := time.Now()
 | 
						|
		err = ctr.StopWithTimeout(2)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		stopTotalTime := time.Now().Sub(stopStartTime)
 | 
						|
 | 
						|
		//Delete Container
 | 
						|
		deleteStartTime := time.Now()
 | 
						|
 | 
						|
		err = client.RemoveContainer(ctx, ctr, true, false)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		deleteTotalTime := time.Now().Sub(deleteStartTime)
 | 
						|
 | 
						|
		data.updateProfileData(createTotalTime, startTotalTime, stopTotalTime, deleteTotalTime)
 | 
						|
	}
 | 
						|
	return data, nil
 | 
						|
}
 | 
						|
 | 
						|
type profileData struct {
 | 
						|
	minCreate, minStart, minStop, minDel time.Duration
 | 
						|
	avgCreate, avgStart, avgStop, avgDel time.Duration
 | 
						|
	maxCreate, maxStart, maxStop, maxDel time.Duration
 | 
						|
}
 | 
						|
 | 
						|
func (data *profileData) updateProfileData(create, start, stop, delete time.Duration) {
 | 
						|
	if create < data.minCreate || data.minCreate == 0 {
 | 
						|
		data.minCreate = create
 | 
						|
	}
 | 
						|
	if create > data.maxCreate || data.maxCreate == 0 {
 | 
						|
		data.maxCreate = create
 | 
						|
	}
 | 
						|
	if start < data.minStart || data.minStart == 0 {
 | 
						|
		data.minStart = start
 | 
						|
	}
 | 
						|
	if start > data.maxStart || data.maxStart == 0 {
 | 
						|
		data.maxStart = start
 | 
						|
	}
 | 
						|
	if stop < data.minStop || data.minStop == 0 {
 | 
						|
		data.minStop = stop
 | 
						|
	}
 | 
						|
	if stop > data.maxStop || data.maxStop == 0 {
 | 
						|
		data.maxStop = stop
 | 
						|
	}
 | 
						|
	if delete < data.minDel || data.minDel == 0 {
 | 
						|
		data.minDel = delete
 | 
						|
	}
 | 
						|
	if delete > data.maxDel || data.maxDel == 0 {
 | 
						|
		data.maxDel = delete
 | 
						|
	}
 | 
						|
 | 
						|
	data.avgCreate = data.avgCreate + create
 | 
						|
	data.avgStart = data.avgStart + start
 | 
						|
	data.avgStop = data.avgStop + stop
 | 
						|
	data.avgDel = data.avgDel + delete
 | 
						|
}
 | 
						|
 | 
						|
func (data *profileData) printProfiledData(testCount float64) {
 | 
						|
 | 
						|
	fmt.Printf("\nProfile data\n\n")
 | 
						|
	w := new(tabwriter.Writer)
 | 
						|
	w.Init(os.Stdout, 0, 8, 0, '\t', 0)
 | 
						|
	fmt.Fprintln(w, "\tCreate\tStart\tStop\tDelete")
 | 
						|
	fmt.Fprintf(w, "Min\t%.2fs\t%.2fs\t%.2fs\t%.2fs\n", data.minCreate.Seconds(), data.minStart.Seconds(), data.minStop.Seconds(), data.minDel.Seconds())
 | 
						|
	fmt.Fprintf(w, "Avg\t%.2fs\t%.2fs\t%.2fs\t%.2fs\n", data.avgCreate.Seconds()/testCount, data.avgStart.Seconds()/testCount, data.avgStop.Seconds()/testCount, data.avgDel.Seconds()/testCount)
 | 
						|
	fmt.Fprintf(w, "Max\t%.2fs\t%.2fs\t%.2fs\t%.2fs\n", data.maxCreate.Seconds(), data.maxStart.Seconds(), data.maxStop.Seconds(), data.maxDel.Seconds())
 | 
						|
	fmt.Fprintf(w, "Total\t%.2fs\t%.2fs\t%.2fs\t%.2fs\n", data.avgCreate.Seconds(), data.avgStart.Seconds(), data.avgStop.Seconds(), data.avgDel.Seconds())
 | 
						|
	fmt.Fprintln(w)
 | 
						|
	w.Flush()
 | 
						|
}
 |