Example #1
0
func TestRandStr(t *testing.T) {
	r1 := utils.RandStr(10, "alphanum")
	r2 := utils.RandStr(10, "alphanum")
	r3 := utils.RandStr(10, "alphanum")

	if r1 == r2 || r1 == r3 || r2 == r3 {
		t.Fatal("The RandStr function should not return same string!")
	}
}
Example #2
0
// Setup lo ip address
// options for operation: add or del
func SetupLoopbackAddress(vm *hypervisor.Vm, container, ip, operation string) error {
	execId := fmt.Sprintf("exec-%s", utils.RandStr(10, "alpha"))
	command := "ip addr " + operation + " dev lo " + ip + "/32"
	execcmd, err := json.Marshal(strings.Split(command, " "))
	if err != nil {
		return err
	}

	tty := &hypervisor.TtyIO{
		Callback: make(chan *types.VmResponse, 1),
	}

	vm.Pod.AddExec(container, execId, command, false)
	defer vm.Pod.DeleteExec(execId)

	if err := vm.Exec(container, execId, string(execcmd), false, tty); err != nil {
		return err
	}

	es := vm.Pod.GetExec(execId)
	if es == nil {
		return fmt.Errorf("cannot find exec status for %s: %s", command, execId)
	}
	if es.ExitCode != 0 {
		return fmt.Errorf("exec %s on container %s failed with exit code %d", command, container, es.ExitCode)
	}

	return nil
}
Example #3
0
// Setup lo ip address
// options for operation: add or del
func SetupLoopbackAddress(vm *hypervisor.Vm, container, ip, operation string) error {
	execId := fmt.Sprintf("exec-%s", utils.RandStr(10, "alpha"))
	command := "ip addr " + operation + " dev lo " + ip + "/32"
	execcmd, err := json.Marshal(strings.Split(command, " "))
	if err != nil {
		return err
	}

	tty := &hypervisor.TtyIO{
		Callback: make(chan *types.VmResponse, 1),
	}

	result := vm.WaitProcess(false, []string{execId}, 60)
	if result == nil {
		return fmt.Errorf("can not wait %s, id: %s", command, execId)
	}

	if err := vm.Exec(container, execId, string(execcmd), false, tty); err != nil {
		return err
	}

	r, ok := <-result
	if !ok {
		return fmt.Errorf("exec failed %s: %s", command, execId)
	}
	if r.Code != 0 {
		return fmt.Errorf("exec %s on container %s failed with exit code %d", command, container, r.Code)
	}

	return nil
}
Example #4
0
File: run.go Project: gnawux/hyper
func (daemon *Daemon) CreatePod(podId string, podSpec *apitypes.UserPod) (*pod.XPod, error) {
	//FIXME: why restrict to 1024
	if daemon.PodList.CountRunning() >= 1024 {
		return nil, fmt.Errorf("There have already been %d running Pods", 1024)
	}
	if podId == "" {
		podId = fmt.Sprintf("pod-%s", utils.RandStr(10, "alpha"))
	}

	if podSpec.Id == "" {
		podSpec.Id = podId
	}

	if _, ok := daemon.PodList.Get(podSpec.Id); ok {
		return nil, fmt.Errorf("pod %s already exist", podSpec.Id)
	}

	if err := podSpec.Validate(); err != nil {
		return nil, err
	}

	factory := pod.NewPodFactory(daemon.Factory, daemon.PodList, daemon.db, daemon.Storage, daemon.Daemon, daemon.DefaultLog)

	p, err := pod.CreateXPod(factory, podSpec)
	if err != nil {
		glog.Errorf("%s: failed to add pod: %v", podSpec.Id, err)
		return nil, err
	}

	return p, nil
}
Example #5
0
File: exec.go Project: gnawux/hyper
func (p *XPod) CreateExec(containerId, cmds string, terminal bool) (string, error) {
	c, ok := p.containers[containerId]
	if !ok {
		err := fmt.Errorf("no container available for exec %s", cmds)
		p.Log(ERROR, err)
		return "", err
	}

	if !c.IsAlive() {
		err := fmt.Errorf("container is not available (%v) for exec %s", c.CurrentState(), cmds)
		p.Log(ERROR, err)
		return "", err
	}

	execId := fmt.Sprintf("exec-%s", utils.RandStr(10, "alpha"))

	p.statusLock.Lock()
	p.execs[execId] = &Exec{
		Container: containerId,
		Id:        execId,
		Cmds:      cmds,
		Terminal:  terminal,
		ExitCode:  255,
		logPrefix: fmt.Sprintf("Pod[%s] Con[%s] Exec[%s] ", p.Id(), containerId[:12], execId),
		finChan:   make(chan bool, 1),
	}
	p.statusLock.Unlock()

	return execId, nil
}
Example #6
0
func imageToName(image string) string {
	name := image
	fields := strings.Split(image, "/")
	if len(fields) > 1 {
		name = fields[len(fields)-1]
	}
	fields = strings.Split(name, ":")
	if len(fields) < 2 {
		name = name + "-" + utils.RandStr(10, "number")
	} else {
		name = fields[0] + "-" + fields[1] + "-" + utils.RandStr(10, "number")
	}

	validContainerNameChars := `[a-zA-Z0-9][a-zA-Z0-9_.-]`
	validContainerNamePattern := regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
	if !validContainerNamePattern.MatchString(name) {
		name = namesgenerator.GetRandomName(0)
	}
	return name
}
Example #7
0
func (daemon *Daemon) CreateExec(containerId, cmd string, terminal bool) (string, error) {
	execId := fmt.Sprintf("exec-%s", utils.RandStr(10, "alpha"))

	glog.V(1).Infof("Get container id is %s", containerId)
	pod, _, err := daemon.GetPodByContainerIdOrName(containerId)
	if err != nil {
		return "", err
	}

	status := pod.Status()
	if status == nil || status.Status != types.S_POD_RUNNING {
		return "", fmt.Errorf("container %s is not running", containerId)
	}

	status.AddExec(containerId, execId, cmd, terminal)
	return execId, nil
}
Example #8
0
File: pod.go Project: juito/hyper
// Override the Docker ContainerCreate interface, create pod to run command
func (d Docker) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) {
	var podString string
	var err error

	if params.Config == nil {
		return types.ContainerCreateResponse{}, derr.ErrorCodeEmptyConfig
	}

	podId := fmt.Sprintf("buildpod-%s", utils.RandStr(10, "alpha"))
	// Hack here, container created by ADD/COPY only has Config
	if params.HostConfig != nil {
		podString, err = MakeBasicPod(podId, params.Config)
	} else {
		podString, err = MakeCopyPod(podId, params.Config)
	}

	if err != nil {
		return types.ContainerCreateResponse{}, err
	}

	var podSpec apitypes.UserPod
	err = json.Unmarshal([]byte(podString), &podSpec)
	if err != nil {
		return types.ContainerCreateResponse{}, err
	}

	pod, err := d.Daemon.CreatePod(podId, &podSpec)
	if err != nil {
		return types.ContainerCreateResponse{}, err
	}

	if len(pod.Status().Containers) != 1 {
		return types.ContainerCreateResponse{}, fmt.Errorf("container count in pod is incorrect")
	}
	cId := pod.Status().Containers[0].Id
	if params.HostConfig != nil {
		d.hyper.BasicPods[cId] = podId
		glog.Infof("basic containerId %s, podId %s", cId, podId)
	} else {
		d.hyper.CopyPods[cId] = podId
		glog.Infof("copy containerId %s, podId %s", cId, podId)
	}

	return types.ContainerCreateResponse{ID: cId}, nil
}
Example #9
0
func ProcessPodBytes(body []byte) (*UserPod, error) {
	var userPod UserPod
	if err := json.Unmarshal(body, &userPod); err != nil {
		return nil, err
	}

	// we need to validate the given POD file
	if userPod.Name == "" {
		userPod.Name = utils.RandStr(10, "alphanum")
	}

	if userPod.Resource.Vcpu == 0 {
		userPod.Resource.Vcpu = 1
	}
	if userPod.Resource.Memory == 0 {
		userPod.Resource.Memory = 128
	}

	var (
		vol UserVolume
		num = 0
	)
	for i, v := range userPod.Containers {
		if v.Image == "" {
			return nil, fmt.Errorf("Please specific your image for your container, it can not be null!\n")
		}
		userPod.Containers[i].Tty = v.Tty || userPod.Tty
		num++
	}
	if num == 0 {
		return nil, fmt.Errorf("Please correct your POD file, the container section can not be null!\n")
	}
	for _, vol = range userPod.Volumes {
		if vol.Name == "" {
			return nil, fmt.Errorf("Hyper ERROR: please specific your volume name, it can not be null!\n")
		}
	}

	if userPod.Labels == nil {
		userPod.Labels = make(map[string]string)
	}

	return &userPod, nil
}
Example #10
0
File: pod.go Project: juito/hyper
func (d Docker) ContainerStart(cId string, hostConfig *containertypes.HostConfig) (err error) {
	var vm *hypervisor.Vm

	podId := ""
	if _, ok := d.hyper.CopyPods[cId]; ok {
		podId = d.hyper.CopyPods[cId]
	} else if _, ok := d.hyper.BasicPods[cId]; ok {
		podId = d.hyper.BasicPods[cId]
	} else {
		return fmt.Errorf("container %s doesn't belong to pod", cId)
	}

	defer func() {
		d.hyper.Ready <- true
		if err != nil && d.hyper.Vm != nil {
			if d.hyper.Status != nil {
				d.hyper.Vm.ReleaseResponseChan(d.hyper.Status)
				d.hyper.Status = nil
			}
			glog.Infof("ContainerStart failed, KillVm")
			d.Daemon.KillVm(d.hyper.Vm.Id)
			d.hyper.Vm = nil
		}
	}()

	vmId := "buildevm-" + utils.RandStr(10, "number")
	if vm, err = d.Daemon.StartVm(vmId, 1, 512, false); err != nil {
		return
	}
	d.hyper.Vm = vm

	if d.hyper.Status, err = vm.GetResponseChan(); err != nil {
		return
	}

	if _, _, err = d.Daemon.StartPod(nil, nil, podId, vm.Id, false); err != nil {
		return
	}

	return nil
}
Example #11
0
func MountVFSVolume(src, sharedDir string) (string, error) {
	var flags uintptr = utils.MS_BIND

	mountSharedDir := utils.RandStr(10, "alpha")
	targetDir := path.Join(sharedDir, mountSharedDir)
	glog.V(1).Infof("trying to bind dir %s to %s", src, targetDir)

	stat, err := os.Stat(src)
	if err != nil {
		glog.Error("Cannot stat volume Source ", err.Error())
		return "", err
	}

	if runtime.GOOS == "linux" {
		base := filepath.Dir(targetDir)
		if err := os.MkdirAll(base, 0755); err != nil && !os.IsExist(err) {
			glog.Errorf("error to create dir %s for volume %s", base, src)
			return "", err
		}

		if stat.IsDir() {
			if err := os.MkdirAll(targetDir, 0755); err != nil && !os.IsExist(err) {
				glog.Errorf("error to create dir %s for volume %s", targetDir, src)
				return "", err
			}
		} else if f, err := os.Create(targetDir); err != nil && !os.IsExist(err) {
			glog.Errorf("error to create file %s for volume %s", targetDir, src)
			return "", err
		} else if err == nil {
			f.Close()
		}
	}

	if err := utils.Mount(src, targetDir, "none", flags, "--bind"); err != nil {
		glog.Errorf("bind dir %s failed: %s", src, err.Error())
		return "", err
	}

	return mountSharedDir, nil
}
Example #12
0
func ServiceDiscoveryContainerName(podName string) string {
	return podName + "-" + utils.RandStr(10, "alpha") + "-service-discovery"
}
Example #13
0
func parseVolume(volStr string) (*pod.UserVolume, *pod.UserVolumeReference, error) {

	var (
		srcName   string
		destPath  string
		volName   string
		readOnly  = false
		volDriver = "vfs"
	)

	fields := strings.Split(volStr, ":")
	if len(fields) == 3 {
		// cmd: -v host-src:container-dest:rw
		srcName = fields[0]
		destPath = fields[1]
		if fields[2] != "ro" && fields[2] != "rw" {
			return nil, nil, fmt.Errorf("flag only support(ro or rw): --volume")
		}
		if fields[2] == "ro" {
			readOnly = true
		}
	} else if len(fields) == 2 {
		// cmd: -v host-src:container-dest
		srcName = fields[0]
		destPath = fields[1]
	} else if len(fields) == 1 {
		// -v container-dest
		destPath = fields[0]
	} else {
		return nil, nil, fmt.Errorf("flag format should be like : --volume=[host-src:]container-dest[:rw|ro]")
	}

	if !strings.HasPrefix(destPath, "/") {
		return nil, nil, fmt.Errorf("The container-dir must always be an absolute path")
	}

	if srcName == "" {
		// Set default volume driver and use destPath as volume Name
		volDriver = ""
		_, volName = filepath.Split(destPath)
	} else {
		srcName, _ = filepath.Abs(srcName)
		_, volName = filepath.Split(srcName)
		// Auto create the source folder on the host , otherwise hyperd will complain
		if _, err := os.Stat(srcName); err != nil && os.IsNotExist(err) {
			if err := os.MkdirAll(srcName, os.FileMode(0777)); err != nil {
				return nil, nil, err
			}
		}
	}

	vol := pod.UserVolume{
		// Avoid name collision
		Name:   volName + utils.RandStr(5, "number"),
		Source: srcName,
		Driver: volDriver,
	}

	volRef := pod.UserVolumeReference{
		Volume:   vol.Name,
		Path:     destPath,
		ReadOnly: readOnly,
	}

	return &vol, &volRef, nil
}
Example #14
0
File: build.go Project: juito/hyper
// hyperctl build [OPTIONS] PATH
func (cli *HyperClient) HyperCmdBuild(args ...string) error {
	var opts struct {
		ImageName      string `long:"tag" short:"t" default:"" value-name:"\"\"" default-mask:"-" description:"Repository name (and optionally a tag) to be applied to the resulting image in case of success"`
		DockerfileName string `long:"file" short:"f" default:"" value-name:"\"\"" default-mask:"-" description:"Customized docker file"`
	}

	var parser = gflag.NewParser(&opts, gflag.Default)
	parser.Usage = "build [OPTIONS] PATH\n\nBuild a new image from the source code at PATH"
	args, err := parser.ParseArgs(args)
	if err != nil {
		if !strings.Contains(err.Error(), "Usage") {
			return err
		} else {
			return nil
		}
	}

	if len(args) == 0 {
		return fmt.Errorf("%s: \"build\" requires a minimum of 1 argument, See 'hyperctl build --help'.", os.Args[0])
	}
	var (
		filename = ""
		context  archive.Archive
		name     = ""
	)
	root := args[0]
	if _, err := os.Stat(root); err != nil {
		return err
	}

	absRoot, err := filepath.Abs(root)
	if err != nil {
		return err
	}

	filename = opts.DockerfileName // path to Dockerfile

	if opts.DockerfileName == "" {
		// No -f/--file was specified so use the default
		opts.DockerfileName = api.DefaultDockerfileName
		filename = filepath.Join(absRoot, opts.DockerfileName)

		// Just to be nice ;-) look for 'dockerfile' too but only
		// use it if we found it, otherwise ignore this check
		if _, err = os.Lstat(filename); os.IsNotExist(err) {
			tmpFN := path.Join(absRoot, strings.ToLower(opts.DockerfileName))
			if _, err = os.Lstat(tmpFN); err == nil {
				opts.DockerfileName = strings.ToLower(opts.DockerfileName)
				filename = tmpFN
			}
		}
	}

	origDockerfile := opts.DockerfileName // used for error msg
	if filename, err = filepath.Abs(filename); err != nil {
		return err
	}

	// Verify that 'filename' is within the build context
	filename, err = symlink.FollowSymlinkInScope(filename, absRoot)
	if err != nil {
		return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root)
	}

	// Now reset the dockerfileName to be relative to the build context
	opts.DockerfileName, err = filepath.Rel(absRoot, filename)
	if err != nil {
		return err
	}
	// And canonicalize dockerfile name to a platform-independent one
	opts.DockerfileName, err = archive.CanonicalTarNameForPath(opts.DockerfileName)
	if err != nil {
		return fmt.Errorf("Cannot canonicalize dockerfile path %s: %v", opts.DockerfileName, err)
	}

	if _, err = os.Lstat(filename); os.IsNotExist(err) {
		return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile)
	}
	var includes = []string{"."}

	f, err := os.Open(filepath.Join(root, ".dockerignore"))
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	defer f.Close()

	var excludes []string
	if err == nil {
		excludes, err = dockerignore.ReadAll(f)
		if err != nil {
			return err
		}
	}

	if err := ValidateContextDirectory(root, excludes); err != nil {
		return fmt.Errorf("Error checking context: '%s'.", err)
	}

	// If .dockerignore mentions .dockerignore or the Dockerfile
	// then make sure we send both files over to the daemon
	// because Dockerfile is, obviously, needed no matter what, and
	// .dockerignore is needed to know if either one needs to be
	// removed.  The deamon will remove them for us, if needed, after it
	// parses the Dockerfile.
	keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
	keepThem2, _ := fileutils.Matches(opts.DockerfileName, excludes)
	if keepThem1 || keepThem2 {
		includes = append(includes, ".dockerignore", opts.DockerfileName)
	}

	if err := ValidateContextDirectory(root, excludes); err != nil {
		return fmt.Errorf("Error checking context: '%s'.", err)
	}
	options := &archive.TarOptions{
		Compression:     archive.Uncompressed,
		ExcludePatterns: excludes,
		IncludeFiles:    includes,
	}
	context, err = archive.TarWithOptions(root, options)
	if err != nil {
		return err
	}
	var body io.Reader
	// Setup an upload progress bar
	// FIXME: ProgressReader shouldn't be this annoying to use
	if context != nil {
		var progBuff io.Writer = cli.out

		progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)

		body = progress.NewProgressReader(context, progressOutput, 0, "", "Sending build context to Docker daemon")
	}

	if opts.ImageName == "" {
		// set a image name
		name = rand.RandStr(10, "alphanum")
	} else {
		name = opts.ImageName
		if _, err := reference.ParseNamed(name); err != nil {
			return err
		}
	}
	output, ctype, err := cli.client.Build(name, context != nil, body)
	if err != nil {
		return err
	}
	return cli.readStreamOutput(output, ctype, false, cli.out, cli.err)
}
Example #15
0
func (cli *HyperClient) GetTag() string {
	return utils.RandStr(8, "alphanum")
}
Example #16
0
func (d *Driver) VmMountLayer(id string) error {
	if daemon == nil {
		if err := d.Setup(); err != nil {
			return err
		}
	}

	var (
		diffSrc = fmt.Sprintf("%s/diff/%s", d.RootPath(), id)
		volDst  = fmt.Sprintf("%s/images/%s.vdi", d.RootPath(), id)
	)
	podstring, err := MakeMountPod("mac-vm-disk-mount-layer", "puller:latest", id, diffSrc, volDst)
	if err != nil {
		return err
	}
	podId := fmt.Sprintf("pull-%s", utils.RandStr(10, "alpha"))
	vmId := fmt.Sprintf("%s-%s", d.pullVm, utils.RandStr(10, "alpha"))

	var podSpec apitypes.UserPod
	err = json.Unmarshal([]byte(podstring), &podSpec)
	if err != nil {
		return err
	}

	p, err := daemon.CreatePod(podId, &podSpec)
	if err != nil {
		glog.Errorf("can not create pod %s", podstring)
		return err
	}
	defer daemon.CleanPod(podId)

	vm, err := d.daemon.StartVm(vmId, 1, 64, false)
	if err != nil {
		glog.Error(err)
		return err
	}

	// wait for cmd finish
	Status, err := vm.GetResponseChan()
	if err != nil {
		d.daemon.KillVm(vmId)
		glog.Error(err)
		return err
	}
	defer vm.ReleaseResponseChan(Status)

	code, cause, err := daemon.StartInternal(p, vmId, nil, false, []*hypervisor.TtyIO{})
	if err != nil {
		d.daemon.KillVm(vmId)
		glog.Errorf("Code is %d, Cause is %s, %s", code, cause, err.Error())
		return err
	}

	var vmResponse *types.VmResponse
	for {
		vmResponse = <-Status
		if vmResponse.VmId == vmId {
			if vmResponse.Code == types.E_VM_SHUTDOWN {
				glog.Infof("vm sthudown")
				break
			}
		}
	}

	return nil
}
Example #17
0
File: pod.go Project: juito/hyper
// TODO: remove convertToRunvPodSpec after pod.UserPod is deleted from runv
func convertToRunvPodSpec(podSpec *apitypes.UserPod) (*pod.UserPod, error) {
	var userPod pod.UserPod

	userPod.Name = podSpec.Id
	if podSpec.Id == "" {
		userPod.Name = utils.RandStr(10, "alphanum")
	}

	if podSpec.PortmappingWhiteLists != nil {
		for _, cidr := range podSpec.PortmappingWhiteLists.ExternalNetworks {
			_, _, err := net.ParseCIDR(cidr)
			if err != nil {
				return nil, fmt.Errorf("PortmappingWhiteLists.ExternalNetwork %s format error", cidr)
			}
		}
		filteredInternalNetworks := make([]string, 0)
		for _, cidr := range podSpec.PortmappingWhiteLists.InternalNetworks {
			_, _, err := net.ParseCIDR(cidr)
			if err != nil {
				return nil, fmt.Errorf("PortmappingWhiteLists.InternalNetworks %s format error", cidr)
			}

			// filter cidr out if the cidr is also in ExternalNetworks
			found := false
			for _, ext := range podSpec.PortmappingWhiteLists.ExternalNetworks {
				if cidr == ext {
					found = true
					break
				}
			}
			if !found {
				filteredInternalNetworks = append(filteredInternalNetworks, cidr)
			}
		}

		userPod.PortmappingWhiteLists = &pod.PortmappingWhiteList{
			InternalNetworks: filteredInternalNetworks,
			ExternalNetworks: podSpec.PortmappingWhiteLists.ExternalNetworks,
		}
	}

	userPod.Hostname = podSpec.Hostname
	userPod.Type = podSpec.Type
	userPod.RestartPolicy = podSpec.RestartPolicy
	userPod.Dns = podSpec.Dns
	userPod.Tty = podSpec.Tty
	userPod.Labels = podSpec.Labels

	if podSpec.Labels == nil {
		userPod.Labels = make(map[string]string)
	}

	if podSpec.Resource != nil {
		userPod.Resource = pod.UserResource{
			Vcpu:   int(podSpec.Resource.Vcpu),
			Memory: int(podSpec.Resource.Memory),
		}
	}
	if userPod.Resource.Vcpu == 0 {
		userPod.Resource.Vcpu = 1
	}
	if userPod.Resource.Memory == 0 {
		userPod.Resource.Memory = 128
	}

	if len(podSpec.Containers) > 0 {
		containers := make([]pod.UserContainer, 0, len(podSpec.Containers))
		for _, v := range podSpec.Containers {
			if v.Image == "" {
				return nil, fmt.Errorf("Please specific your image for your container, it can not be null!\n")
			}

			containers = append(containers, convertToRunvContainerSpec(v, userPod.Tty))
		}

		userPod.Containers = containers
	}

	if len(podSpec.Files) > 0 {
		files := make([]pod.UserFile, 0, len(podSpec.Files))
		for _, f := range podSpec.Files {
			files = append(files, pod.UserFile{
				Name:     f.Name,
				Encoding: f.Encoding,
				Uri:      f.Uri,
				Contents: f.Content,
			})
		}
		userPod.Files = files
	}

	if len(podSpec.Volumes) > 0 {
		vols := make([]pod.UserVolume, 0, len(podSpec.Volumes))
		for _, vol := range podSpec.Volumes {
			if vol.Name == "" {
				return nil, fmt.Errorf("Hyper ERROR: please specific your volume name, it can not be null!\n")
			}

			v := pod.UserVolume{
				Name:   vol.Name,
				Driver: vol.Driver,
				Source: vol.Source,
			}
			if vol.Option != nil {
				v.Option = pod.UserVolumeOption{
					Monitors: vol.Option.Monitors,
					Keyring:  vol.Option.Keyring,
					User:     vol.Option.User,
				}
			}
			vols = append(vols, v)
		}
		userPod.Volumes = vols
	}

	if len(podSpec.Services) > 0 {
		services := make([]pod.UserService, 0, len(podSpec.Services))
		for _, svc := range podSpec.Services {
			s := pod.UserService{
				ServiceIP:   svc.ServiceIP,
				ServicePort: int(svc.ServicePort),
				Protocol:    svc.Protocol,
			}
			if len(svc.Hosts) > 0 {
				hosts := make([]pod.UserServiceBackend, 0, len(svc.Hosts))
				for _, host := range svc.Hosts {
					hosts = append(hosts, pod.UserServiceBackend{
						HostIP:   host.HostIP,
						HostPort: int(host.HostPort),
					})
				}
				s.Hosts = hosts
			}
			services = append(services, s)
		}
		userPod.Services = services
	}

	if len(podSpec.Interfaces) > 0 {
		interfaces := make([]pod.UserInterface, 0, len(podSpec.Interfaces))
		for _, i := range podSpec.Interfaces {
			interfaces = append(interfaces, pod.UserInterface{
				Bridge: i.Bridge,
				Ip:     i.Ip,
				Ifname: i.Ifname,
				Mac:    i.Mac,
				Gw:     i.Gateway,
			})
		}
		userPod.Interfaces = interfaces
	}

	if podSpec.Log != nil {
		userPod.LogConfig = pod.PodLogConfig{
			Type:   podSpec.Log.Type,
			Config: podSpec.Log.Config,
		}
	}

	return &userPod, nil
}