Example #1
0
func getImageTags(templateRepos []string, serviceRepos []string, tags []string) (map[string][]string, error) {
	imagemap := make(map[string][]string)

	// find all the template repos
	for _, repo := range templateRepos {
		image, err := docker.FindImage(repo, false)
		if err == docker.ErrNoSuchImage {
			glog.Warningf("Could not find template image %s", repo)
			continue
		} else if err != nil {
			glog.Errorf("Could not look up repo %s: %s", repo, err)
			return nil, err
		}
		if image.ID.Tag == DockerLatest {
			image.ID.Tag = ""
		}
		images := imagemap[image.UUID]
		imagemap[image.UUID] = append(images, image.ID.String())
	}

	// find all the service repos
	for _, repo := range serviceRepos {
		image, err := docker.FindImage(repo, false)
		if err != nil {
			glog.Errorf("Could not look up repo %s: %s", repo, err)
			return nil, err
		}
		if image.ID.Tag == DockerLatest {
			image.ID.Tag = ""
		}
		images := imagemap[image.UUID]
		imagemap[image.UUID] = append(images, image.ID.String())

		for _, tag := range tags {
			image, err := docker.FindImage(commons.JoinRepoTag(repo, tag), false)
			if err == docker.ErrNoSuchImage {
				continue
			} else if err != nil {
				glog.Errorf("Could not look up repo %s: %s", commons.JoinRepoTag(repo, tag), err)
				return nil, err
			}
			images := imagemap[image.UUID]
			imagemap[image.UUID] = append(images, image.ID.String())
		}
	}

	return imagemap, nil
}
Example #2
0
func loadImage(filename string, uuid string, tags []string) error {
	// look up the image by UUID
	images, err := docker.Images()
	if err != nil {
		glog.Errorf("Could not look up images: %s", err)
		return err
	}

	var image *docker.Image
	for _, i := range images {
		if i.UUID == uuid {
			image = i
			break
		}
	}

	// image not found so import
	if image == nil {
		glog.Warningf("Importing image from file, don't forget to sync (serviced docker sync)")
		if err := docker.ImportImage(tags[0], filename); err != nil {
			glog.Errorf("Could not import image from file %s: %s", filename, err)
			return err
		} else if image, err = docker.FindImage(tags[0], false); err != nil {
			glog.Errorf("Could not look up docker image %s: %s", tags[0], err)
			return err
		}
		glog.Infof("Tagging images %v at %s", tags, image.UUID)
		tags = tags[1:]
	}

	// tag the remaining images
	for _, tag := range tags {
		if _, err := image.Tag(tag); err != nil {
			glog.Errorf("Could not tag image %s as %s: %s", image.UUID, tag, err)
			return err
		}
	}

	return nil
}
Example #3
0
func (f *Facade) deployServiceDefinitions(ctx datastore.Context, sds []servicedefinition.ServiceDefinition, pool string, parentServiceID string, volumes map[string]string, deploymentId string, tenantId *string) error {
	// ensure that all images in the templates exist
	imageIds := make(map[string]struct{})
	for _, svc := range sds {
		getSubServiceImageIDs(imageIds, svc)
	}

	for imageId, _ := range imageIds {
		_, err := docker.FindImage(imageId, false)
		if err != nil {
			msg := fmt.Errorf("could not look up image %s: %s. Check your docker login and retry service deployment.", imageId, err)
			glog.Error(err.Error())
			return msg
		}
	}

	for _, sd := range sds {
		if _, err := f.deployServiceDefinition(ctx, sd, pool, parentServiceID, volumes, deploymentId, tenantId); err != nil {
			return err
		}
	}
	return nil
}
Example #4
0
// configureContainer creates and populates two structures, a docker client Config and a docker client HostConfig structure
// that are used to create and start a container respectively. The information used to populate the structures is pulled from
// the service, serviceState, and conn values that are passed into configureContainer.
func configureContainer(a *HostAgent, client *ControlClient,
	svc *service.Service, serviceState *servicestate.ServiceState,
	virtualAddressSubnet string) (*dockerclient.Config, *dockerclient.HostConfig, error) {
	cfg := &dockerclient.Config{}
	hcfg := &dockerclient.HostConfig{}

	//get this service's tenantId for volume mapping
	var tenantID string
	err := client.GetTenantId(svc.ID, &tenantID)
	if err != nil {
		glog.Errorf("Failed getting tenantID for service: %s, %s", svc.ID, err)
		return nil, nil, err
	}

	// get the system user
	unused := 0
	systemUser := user.User{}
	err = client.GetSystemUser(unused, &systemUser)
	if err != nil {
		glog.Errorf("Unable to get system user account for agent %s", err)
		return nil, nil, err
	}
	glog.V(1).Infof("System User %v", systemUser)

	cfg.Image = svc.ImageID

	// get the endpoints
	cfg.ExposedPorts = make(map[dockerclient.Port]struct{})
	hcfg.PortBindings = make(map[dockerclient.Port][]dockerclient.PortBinding)

	if svc.Endpoints != nil {
		glog.V(1).Info("Endpoints for service: ", svc.Endpoints)
		for _, endpoint := range svc.Endpoints {
			if endpoint.Purpose == "export" { // only expose remote endpoints
				var port uint16
				port = endpoint.PortNumber
				if endpoint.PortTemplate != "" {
					t := template.Must(template.New("PortTemplate").Funcs(funcmap).Parse(endpoint.PortTemplate))
					b := bytes.Buffer{}
					err := t.Execute(&b, serviceState)
					if err == nil {
						j, err := strconv.Atoi(b.String())
						if err != nil {
							glog.Errorf("%+v", err)
						} else if j > 0 {
							port = uint16(j)
						}
					}
				}
				var p string
				switch endpoint.Protocol {
				case commons.UDP:
					p = fmt.Sprintf("%d/%s", port, "udp")
				default:
					p = fmt.Sprintf("%d/%s", port, "tcp")
				}
				cfg.ExposedPorts[dockerclient.Port(p)] = struct{}{}
				hcfg.PortBindings[dockerclient.Port(p)] = append(hcfg.PortBindings[dockerclient.Port(p)], dockerclient.PortBinding{})
			}
		}
	}

	if len(tenantID) == 0 && len(svc.Volumes) > 0 {
		// FIXME: find a better way of handling this error condition
		glog.Fatalf("Could not get tenant ID and need to mount a volume, service state: %s, service id: %s", serviceState.ID, svc.ID)
	}

	// Make sure the image exists locally.
	if _, err = docker.FindImage(svc.ImageID, true); err != nil {
		glog.Errorf("can't find docker image %s: %s", svc.ImageID, err)
		return nil, nil, err
	}

	cfg.Volumes = make(map[string]struct{})
	hcfg.Binds = []string{}

	if err := injectContext(svc, serviceState, client); err != nil {
		glog.Errorf("Error injecting context: %s", err)
		return nil, nil, err
	}

	for _, volume := range svc.Volumes {
		if volume.Type != "" && volume.Type != "dfs" {
			continue
		}

		resourcePath, err := a.setupVolume(tenantID, svc, volume)
		if err != nil {
			return nil, nil, err
		}

		binding := fmt.Sprintf("%s:%s", resourcePath, volume.ContainerPath)
		cfg.Volumes[strings.Split(binding, ":")[1]] = struct{}{}
		hcfg.Binds = append(hcfg.Binds, strings.TrimSpace(binding))
	}

	dir, binary, err := ExecPath()
	if err != nil {
		glog.Errorf("Error getting exec path: %v", err)
		return nil, nil, err
	}
	volumeBinding := fmt.Sprintf("%s:/serviced", dir)
	cfg.Volumes[strings.Split(volumeBinding, ":")[1]] = struct{}{}
	hcfg.Binds = append(hcfg.Binds, strings.TrimSpace(volumeBinding))

	// bind mount everything we need for logstash-forwarder
	if len(svc.LogConfigs) != 0 {
		const LOGSTASH_CONTAINER_DIRECTORY = "/usr/local/serviced/resources/logstash"
		logstashPath := utils.ResourcesDir() + "/logstash"
		binding := fmt.Sprintf("%s:%s", logstashPath, LOGSTASH_CONTAINER_DIRECTORY)
		cfg.Volumes[LOGSTASH_CONTAINER_DIRECTORY] = struct{}{}
		hcfg.Binds = append(hcfg.Binds, binding)
		glog.V(1).Infof("added logstash bind mount: %s", binding)
	}

	// specify temporary volume paths for docker to create
	tmpVolumes := []string{"/tmp"}
	for _, volume := range svc.Volumes {
		if volume.Type == "tmp" {
			tmpVolumes = append(tmpVolumes, volume.ContainerPath)
		}
	}
	for _, path := range tmpVolumes {
		cfg.Volumes[path] = struct{}{}
		glog.V(4).Infof("added temporary docker container path: %s", path)
	}

	// add arguments to mount requested directory (if requested)
	glog.V(2).Infof("Checking Mount options for service %#v", svc)
	for _, bindMountString := range a.mount {
		glog.V(2).Infof("bindmount is  %#v", bindMountString)
		splitMount := strings.Split(bindMountString, ",")
		numMountArgs := len(splitMount)

		if numMountArgs == 2 || numMountArgs == 3 {

			requestedImage := splitMount[0]
			glog.V(2).Infof("mount requestedImage %#v", requestedImage)
			hostPath := splitMount[1]
			glog.V(2).Infof("mount hostPath %#v", hostPath)
			// assume the container path is going to be the same as the host path
			containerPath := hostPath

			// if the container path is provided, use it
			if numMountArgs > 2 {
				containerPath = splitMount[2]
			}
			glog.V(2).Infof("mount containerPath %#v", containerPath)

			// insert tenantId into requestedImage - see facade.DeployService
			matchedRequestedImage := false
			if requestedImage == "*" {
				matchedRequestedImage = true
			} else {
				imageID, err := commons.ParseImageID(requestedImage)
				if err != nil {
					glog.Errorf("error parsing imageid %v: %v", requestedImage, err)
					continue
				}
				svcImageID, err := commons.ParseImageID(svc.ImageID)
				if err != nil {
					glog.Errorf("error parsing service imageid %v; %v", svc.ImageID, err)
					continue
				}
				glog.V(2).Infof("mount checking %#v and %#v ", imageID, svcImageID)
				matchedRequestedImage = (imageID.Repo == svcImageID.Repo)
			}

			if matchedRequestedImage {
				binding := fmt.Sprintf("%s:%s", hostPath, containerPath)
				cfg.Volumes[strings.Split(binding, ":")[1]] = struct{}{}
				hcfg.Binds = append(hcfg.Binds, strings.TrimSpace(binding))
			}
		} else {
			glog.Warningf("Could not bind mount the following: %s", bindMountString)
		}
	}

	// Get host IP
	ips, err := utils.GetIPv4Addresses()
	if err != nil {
		glog.Errorf("Error getting host IP addresses: %v", err)
		return nil, nil, err
	}

	// add arguments for environment variables
	cfg.Env = append([]string{},
		fmt.Sprintf("CONTROLPLANE_SYSTEM_USER=%s", systemUser.Name),
		fmt.Sprintf("CONTROLPLANE_SYSTEM_PASSWORD=%s", systemUser.Password),
		fmt.Sprintf("CONTROLPLANE_HOST_IPS='%s'", strings.Join(ips, " ")),
		fmt.Sprintf("SERVICED_VIRTUAL_ADDRESS_SUBNET=%s", virtualAddressSubnet),
		fmt.Sprintf("SERVICED_IS_SERVICE_SHELL=false"),
		fmt.Sprintf("SERVICED_NOREGISTRY=%s", os.Getenv("SERVICED_NOREGISTRY")),
		fmt.Sprintf("SERVICED_SERVICE_IMAGE=%s", svc.ImageID),
		fmt.Sprintf("SERVICED_MAX_RPC_CLIENTS=1"),
		fmt.Sprintf("SERVICED_RPC_PORT=%s", a.rpcport),
		fmt.Sprintf("TZ=%s", os.Getenv("TZ")))

	// add dns values to setup
	for _, addr := range a.dockerDNS {
		_addr := strings.TrimSpace(addr)
		if len(_addr) > 0 {
			cfg.Dns = append(cfg.Dns, addr)
		}
	}

	// Add hostname if set
	if svc.Hostname != "" {
		cfg.Hostname = svc.Hostname
	}

	cfg.Cmd = append([]string{},
		fmt.Sprintf("/serviced/%s", binary),
		"service",
		"proxy",
		svc.ID,
		strconv.Itoa(serviceState.InstanceID),
		svc.Startup)

	if svc.Privileged {
		hcfg.Privileged = true
	}

	// Memory and CpuShares should never be negative
	if svc.MemoryLimit < 0 {
		cfg.Memory = 0
	} else {
		cfg.Memory = svc.MemoryLimit
	}

	if svc.CPUShares < 0 {
		cfg.CpuShares = 0
	} else {
		cfg.CpuShares = svc.CPUShares
	}

	return cfg, hcfg, nil
}
Example #5
0
func StartDocker(cfg *ProcessConfig, port string) (*exec.Cmd, error) {
	var svc service.Service

	// Create a control center client to look up the service
	cp, err := node.NewControlClient(port)
	if err != nil {
		glog.Errorf("could not create a control center client %v", err)
		return nil, err
	}
	glog.Infof("Connected to the control center at port %s", port)

	if err := cp.GetService(cfg.ServiceID, &svc); err != nil {
		glog.Errorf("unable to find service %s", cfg.ServiceID)
		return nil, err
	}

	// make sure docker image is present
	if _, err = docker.FindImage(svc.ImageID, false); err != nil {
		if err == docker.ErrNoSuchImage {
			if err := docker.PullImage(svc.ImageID); err != nil {
				glog.Errorf("unable to pull image %s: %s", svc.ImageID, err)
				return nil, err
			}
		} else {
			glog.Errorf("unable to inspect image %s: %s", svc.ImageID, err)
			return nil, err
		}
	}

	// bind mount on /serviced
	dir, bin, err := node.ExecPath()
	if err != nil {
		glog.Errorf("serviced not found: %s", err)
		return nil, err
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// get the shell command
	shellcmd := cfg.Command
	if cfg.Command == "" {
		shellcmd = "su -"
	}

	// get the serviced command
	svcdcmd := fmt.Sprintf("/serviced/%s", bin)

	// get the proxy command
	proxycmd := []string{
		svcdcmd,
		fmt.Sprintf("--logtostderr=%t", cfg.LogToStderr),
		"service",
		"proxy",
		"--autorestart=false",
		"--disable-metric-forwarding",
		fmt.Sprintf("--logstash=%t", cfg.LogStash.Enable),
		fmt.Sprintf("--logstash-idle-flush-time=%s", cfg.LogStash.IdleFlushTime),
		fmt.Sprintf("--logstash-settle-time=%s", cfg.LogStash.SettleTime),
		svc.ID,
		"0",
		shellcmd,
	}

	// get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		glog.Errorf("Docker not found: %v", err)
		return nil, err
	}
	argv := []string{"run", "-v", servicedVolume, "-v", pwdVolume, "-v", utils.ResourcesDir() + ":" + "/usr/local/serviced/resources"}
	for _, mount := range cfg.Mount {
		hostPath, containerPath, err := parseMountArg(mount)
		if err != nil {
			return nil, err
		}
		argv = append(argv, "-v", fmt.Sprintf("%s:%s", hostPath, containerPath))
	}

	argv = append(argv, cfg.Envv...)

	if cfg.SaveAs != "" {
		argv = append(argv, fmt.Sprintf("--name=%s", cfg.SaveAs))
	} else {
		argv = append(argv, "--rm")
	}

	if cfg.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	// set the systemuser and password
	unused := 0
	systemUser := user.User{}
	err = cp.GetSystemUser(unused, &systemUser)
	if err != nil {
		glog.Errorf("Unable to get system user account for client %s", err)
	}
	argv = append(argv, "-e", fmt.Sprintf("CONTROLPLANE_SYSTEM_USER=%s ", systemUser.Name))
	argv = append(argv, "-e", fmt.Sprintf("CONTROLPLANE_SYSTEM_PASSWORD=%s ", systemUser.Password))
	argv = append(argv, "-e", fmt.Sprintf("SERVICED_NOREGISTRY=%s", os.Getenv("SERVICED_NOREGISTRY")))
	argv = append(argv, "-e", fmt.Sprintf("SERVICED_IS_SERVICE_SHELL=true"))
	argv = append(argv, "-e", fmt.Sprintf("SERVICED_SERVICE_IMAGE=%s", svc.ImageID))

	argv = append(argv, svc.ImageID)
	argv = append(argv, proxycmd...)

	// wait for the DFS to be ready in order to start container on the latest image
	glog.Infof("Acquiring image from the dfs...")
	cp.ReadyDFS(false, nil)
	glog.Infof("Acquired!  Starting shell")

	glog.V(1).Infof("command: docker %+v", argv)
	return exec.Command(docker, argv...), nil
}
Example #6
0
func (f *Facade) deployServiceDefinition(ctx datastore.Context, sd servicedefinition.ServiceDefinition, pool string, parentServiceID string, volumes map[string]string, deploymentId string, tenantId *string) (string, error) {
	// Always deploy in stopped state, starting is a separate step
	ds := int(service.SVCStop)

	exportedVolumes := make(map[string]string)
	for k, v := range volumes {
		exportedVolumes[k] = v
	}
	svc, err := service.BuildService(sd, parentServiceID, pool, ds, deploymentId)
	if err != nil {
		return "", err
	}

	UpdateDeployTemplateStatus(deploymentId, "deploy_loading_service|"+svc.Name)
	getSvc := func(svcID string) (service.Service, error) {
		svc, err := f.GetService(ctx, svcID)
		if err != nil {
			return service.Service{}, err
		}
		return *svc, err
	}
	findChild := func(svcID, childName string) (service.Service, error) {
		svc, err := f.FindChildService(ctx, svcID, childName)
		if err != nil {
			return service.Service{}, err
		}
		return *svc, err
	}

	//for each endpoint, evaluate its Application
	if err = svc.EvaluateEndpointTemplates(getSvc, findChild); err != nil {
		return "", err
	}

	//for each endpoint, evaluate its Application
	if err = svc.EvaluateEndpointTemplates(getSvc, findChild); err != nil {
		return "", err
	}

	if parentServiceID == "" {
		*tenantId = svc.ID
	}

	// Using the tenant id, tag the base image with the tenantID
	if svc.ImageID != "" {
		UpdateDeployTemplateStatus(deploymentId, "deploy_renaming_image|"+svc.Name)
		name, err := renameImageID(f.dockerRegistry, svc.ImageID, *tenantId)
		if err != nil {
			glog.Errorf("malformed imageId: %s", svc.ImageID)
			return "", err
		}

		_, err = docker.FindImage(name, false)
		if err != nil {
			if err != docker.ErrNoSuchImage && !strings.HasPrefix(err.Error(), "No such id:") {
				glog.Error(err)
				return "", err
			}
			UpdateDeployTemplateStatus(deploymentId, "deploy_loading_image|"+name)
			image, err := docker.FindImage(svc.ImageID, false)
			if err != nil {
				msg := fmt.Errorf("could not look up image %s: %s. Check your docker login and retry application deployment.", svc.ImageID, err)
				glog.Error(err.Error())
				return "", msg
			}
			UpdateDeployTemplateStatus(deploymentId, "deploy_tagging_image|"+name)
			if _, err := image.Tag(name); err != nil {
				glog.Errorf("could not tag image: %s (%v)", image.ID, err)
				return "", err
			}
		}
		svc.ImageID = name
	}

	err = f.AddService(ctx, *svc)
	if err != nil {
		return "", err
	}

	return svc.ID, f.deployServiceDefinitions(ctx, sd.Services, pool, svc.ID, exportedVolumes, deploymentId, tenantId)
}
Example #7
0
// Commit will merge a container into existing services' image
func (dfs *DistributedFilesystem) Commit(dockerID string) (string, error) {
	// get the container
	ctr, err := docker.FindContainer(dockerID)
	if err != nil {
		glog.Errorf("Could not get container %s: %s", dockerID, err)
		return "", err
	}

	// verify the container is not currently running
	if ctr.IsRunning() {
		err := fmt.Errorf("cannot commit a running container")
		glog.Errorf("Error committing container %s: %s", ctr.ID, err)
		return "", err
	}

	// find the image to commit (ctr.Config.Image is the repotag)
	image, err := docker.FindImage(ctr.Config.Image, false)
	if err != nil {
		glog.Errorf("Could not find image %s from %s: %s", ctr.Config.Image, dockerID, err)
		return "", err
	}

	// verify the container is not stale (ctr.Image is the UUID)
	if !image.ID.IsLatest() || image.UUID != ctr.Image {
		err := fmt.Errorf("cannot commit a stale container")
		glog.Errorf("Could not commit %s (%s): %s", dockerID, image.ID, err)
		return "", err
	}

	// verify the tenantID
	tenantID, err := dfs.facade.GetTenantID(datastore.Get(), image.ID.User)
	if err != nil {
		glog.Errorf("Could not look up tenant %s from image %s for container %s: %s", image.ID.User, image.ID, dockerID, err)
		return "", err
	} else if tenantID != image.ID.User {
		err := fmt.Errorf("service is not the tenant")
		glog.Errorf("Could not commit %s (%s): %s", dockerID, image.ID, err)
		return "", err
	}

	// check the number of image layers
	if layers, err := image.History(); err != nil {
		glog.Errorf("Could not check history for image %s: %s", image.ID, err)
		return "", err
	} else if numLayers := len(layers); numLayers >= layer.WARN_LAYER_COUNT {
		glog.Warningf("Image %s has %d layers and is approaching the maximum (%d). Please squash image layers.",
			image.ID, numLayers, layer.MAX_LAYER_COUNT)
	} else {
		glog.V(3).Infof("Image %s has %d layers", image.ID, numLayers)
	}

	// commit the container to the image and tag
	newImage, err := ctr.Commit(image.ID.BaseName())
	if err != nil {
		glog.Errorf("Could not commit %s (%s): %s", dockerID, image.ID, err)
		return "", err
	}

	// desynchronize any running containers
	if err := dfs.desynchronize(newImage); err != nil {
		glog.Warningf("Could not denote all desynchronized services: %s", err)
	}

	// snapshot the filesystem and images
	snapshotID, err := dfs.Snapshot(tenantID)
	if err != nil {
		glog.Errorf("Could not create a snapshot of the new image %s: %s", tenantID, err)
		return "", err
	}

	return snapshotID, nil
}