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!") } }
// 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 }
// 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 }
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 }
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 }
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 }
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 }
// 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 }
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 }
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 }
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 }
func ServiceDiscoveryContainerName(podName string) string { return podName + "-" + utils.RandStr(10, "alpha") + "-service-discovery" }
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 }
// 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) }
func (cli *HyperClient) GetTag() string { return utils.RandStr(8, "alphanum") }
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 }
// 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 }