Exemple #1
0
func (s *S) TestDeleteAllAppImageNamesSimilarApps(c *check.C) {
	data := map[string]interface{}{"healthcheck": map[string]interface{}{"path": "/test"}}
	err := image.AppendAppImageName("myapp", "tsuru/app-myapp:v1")
	c.Assert(err, check.IsNil)
	err = image.SaveImageCustomData("tsuru/app-myapp:v1", data)
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("myapp-dev", "tsuru/app-myapp-dev:v1")
	c.Assert(err, check.IsNil)
	err = image.SaveImageCustomData("tsuru/app-myapp-dev:v1", data)
	c.Assert(err, check.IsNil)
	err = image.DeleteAllAppImageNames("myapp")
	c.Assert(err, check.IsNil)
	_, err = image.ListAppImages("myapp")
	c.Assert(err, check.ErrorMatches, "not found")
	_, err = image.ListAppImages("myapp-dev")
	c.Assert(err, check.IsNil)
	yamlData, err := image.GetImageTsuruYamlData("tsuru/app-myapp:v1")
	c.Assert(err, check.IsNil)
	c.Assert(yamlData, check.DeepEquals, provision.TsuruYamlData{})
	yamlData, err = image.GetImageTsuruYamlData("tsuru/app-myapp-dev:v1")
	c.Assert(err, check.IsNil)
	c.Assert(yamlData, check.DeepEquals, provision.TsuruYamlData{
		Healthcheck: provision.TsuruYamlHealthcheck{Path: "/test"},
	})
}
Exemple #2
0
func (s *S) TestDeleteAllAppImageNamesRemovesCustomDataWithoutImages(c *check.C) {
	imgName := "tsuru/app-myapp:v1"
	data := map[string]interface{}{"healthcheck": map[string]interface{}{"path": "/test"}}
	err := image.SaveImageCustomData(imgName, data)
	c.Assert(err, check.IsNil)
	err = image.DeleteAllAppImageNames("myapp")
	c.Assert(err, check.ErrorMatches, "not found")
	yamlData, err := image.GetImageTsuruYamlData(imgName)
	c.Assert(err, check.IsNil)
	c.Assert(yamlData, check.DeepEquals, provision.TsuruYamlData{})
}
Exemple #3
0
func (p *dockerProvisioner) runRestartAfterHooks(cont *container.Container, w io.Writer) error {
	yamlData, err := image.GetImageTsuruYamlData(cont.Image)
	if err != nil {
		return err
	}
	cmds := yamlData.Hooks.Restart.After
	for _, cmd := range cmds {
		err := cont.Exec(p, w, w, cmd)
		if err != nil {
			return errors.Wrapf(err, "couldn't execute restart:after hook %q(%s)", cmd, cont.ShortID())
		}
	}
	return nil
}
Exemple #4
0
func (s *S) TestPullAppImageNamesRemovesCustomData(c *check.C) {
	img1Name := "tsuru/app-myapp:v1"
	err := image.AppendAppImageName("myapp", img1Name)
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("myapp", "tsuru/app-myapp:v2")
	c.Assert(err, check.IsNil)
	err = image.AppendAppImageName("myapp", "tsuru/app-myapp:v3")
	c.Assert(err, check.IsNil)
	data := map[string]interface{}{"healthcheck": map[string]interface{}{"path": "/test"}}
	err = image.SaveImageCustomData(img1Name, data)
	c.Assert(err, check.IsNil)
	err = image.PullAppImageNames("myapp", []string{img1Name})
	c.Assert(err, check.IsNil)
	images, err := image.ListAppImages("myapp")
	c.Assert(err, check.IsNil)
	c.Assert(images, check.DeepEquals, []string{"tsuru/app-myapp:v2", "tsuru/app-myapp:v3"})
	yamlData, err := image.GetImageTsuruYamlData(img1Name)
	c.Assert(err, check.IsNil)
	c.Assert(yamlData, check.DeepEquals, provision.TsuruYamlData{})
}
Exemple #5
0
func LeanContainerCmdsWithExtra(processName, imageId string, app provision.App, extraCmds []string) ([]string, string, error) {
	processCmd, processName, err := ProcessCmdForImage(processName, imageId)
	if err != nil {
		return nil, "", err
	}
	if len(processCmd) == 0 {
		// Legacy support, no processes are yet registered for this app's
		// containers.
		var cmds []string
		cmds, err = runWithAgentCmds(app)
		return cmds, "", err
	}
	yamlData, err := image.GetImageTsuruYamlData(imageId)
	if err != nil {
		return nil, "", err
	}
	extraCmds = append(extraCmds, yamlData.Hooks.Restart.Before...)
	before := strings.Join(extraCmds, " && ")
	if before != "" {
		before += " && "
	}
	if processName == "" {
		processName = "web"
	}
	allCmds := []string{
		"/bin/sh",
		"-lc",
		"[ -d /home/application/current ] && cd /home/application/current; " + before,
	}
	if len(processCmd) > 1 {
		allCmds[len(allCmds)-1] += "exec $0 \"[email protected]\""
		allCmds = append(allCmds, processCmd...)
	} else {
		allCmds[len(allCmds)-1] += "exec " + processCmd[0]
	}
	return allCmds, processName, nil
}
Exemple #6
0
	OnError: rollbackNotice,
	Forward: func(ctx action.FWContext) (action.Result, error) {
		args := ctx.Params[0].(changeUnitsPipelineArgs)
		if err := checkCanceled(args.event); err != nil {
			return nil, err
		}
		newContainers := ctx.Previous.([]container.Container)
		r, err := getRouterForApp(args.app)
		if err != nil {
			return nil, err
		}
		hcRouter, ok := r.(router.CustomHealthcheckRouter)
		if !ok {
			return newContainers, nil
		}
		yamlData, err := image.GetImageTsuruYamlData(args.imageId)
		if err != nil {
			return nil, err
		}
		writer := args.writer
		if writer == nil {
			writer = ioutil.Discard
		}
		hcData := yamlData.Healthcheck.ToRouterHC()
		msg := fmt.Sprintf("Path: %s", hcData.Path)
		if hcData.Status != 0 {
			msg = fmt.Sprintf("%s, Status: %d", msg, hcData.Status)
		}
		if hcData.Body != "" {
			msg = fmt.Sprintf("%s, Body: %s", msg, hcData.Body)
		}
Exemple #7
0
func runHealthcheck(cont *container.Container, w io.Writer) error {
	yamlData, err := image.GetImageTsuruYamlData(cont.Image)
	if err != nil {
		return err
	}
	path := yamlData.Healthcheck.Path
	method := yamlData.Healthcheck.Method
	match := yamlData.Healthcheck.Match
	status := yamlData.Healthcheck.Status
	allowedFailures := yamlData.Healthcheck.AllowedFailures
	if path == "" {
		return nil
	}
	path = strings.TrimSpace(strings.TrimLeft(path, "/"))
	if method == "" {
		method = "get"
	}
	method = strings.ToUpper(method)
	if status == 0 && match == "" {
		status = 200
	}
	var matchRE *regexp.Regexp
	if match != "" {
		match = "(?s)" + match
		matchRE, err = regexp.Compile(match)
		if err != nil {
			return err
		}
	}
	maxWaitTime, _ := config.GetInt("docker:healthcheck:max-time")
	if maxWaitTime == 0 {
		maxWaitTime = 120
	}
	maxWaitTime = maxWaitTime * int(time.Second)
	sleepTime := 3 * time.Second
	startedTime := time.Now()
	url := fmt.Sprintf("http://%s:%s/%s", cont.HostAddr, cont.HostPort, path)
	for {
		var lastError error = nil
		req, err := http.NewRequest(method, url, nil)
		if err != nil {
			return err
		}
		rsp, err := net.Dial5Full60ClientNoKeepAlive.Do(req)
		if err != nil {
			lastError = errors.Wrapf(err, "healthcheck fail(%s)", cont.ShortID())
		} else {
			defer rsp.Body.Close()
			if status != 0 && rsp.StatusCode != status {
				lastError = errors.Errorf("healthcheck fail(%s): wrong status code, expected %d, got: %d", cont.ShortID(), status, rsp.StatusCode)
			} else if matchRE != nil {
				result, err := ioutil.ReadAll(rsp.Body)

				if err != nil {
					lastError = err
				}
				if !matchRE.Match(result) {
					lastError = errors.Errorf("healthcheck fail(%s): unexpected result, expected %q, got: %s", cont.ShortID(), match, string(result))
				}
			}
			if lastError != nil {
				if allowedFailures == 0 {
					return lastError
				}
				allowedFailures--
			}
		}
		if lastError == nil {
			fmt.Fprintf(w, " ---> healthcheck successful(%s)\n", cont.ShortID())
			return nil
		}
		if time.Since(startedTime) > time.Duration(maxWaitTime) {
			return lastError
		}
		fmt.Fprintf(w, " ---> %s. Trying again in %s\n", lastError.Error(), sleepTime)
		time.Sleep(sleepTime)
	}
}
Exemple #8
0
func serviceSpecForApp(opts tsuruServiceOpts) (*swarm.ServiceSpec, error) {
	var envs []string
	for _, envData := range opts.app.Envs() {
		envs = append(envs, fmt.Sprintf("%s=%s", envData.Name, envData.Value))
	}
	host, _ := config.GetString("host")
	envs = append(envs, fmt.Sprintf("%s=%s", "TSURU_HOST", host))
	var cmds []string
	var err error
	var endpointSpec *swarm.EndpointSpec
	var networks []swarm.NetworkAttachmentConfig
	var healthConfig *container.HealthConfig
	port := dockercommon.WebProcessDefaultPort()
	portInt, _ := strconv.Atoi(port)
	if !opts.isDeploy && !opts.isIsolatedRun {
		envs = append(envs, []string{
			fmt.Sprintf("%s=%s", "port", port),
			fmt.Sprintf("%s=%s", "PORT", port),
		}...)
		endpointSpec = &swarm.EndpointSpec{
			Mode: swarm.ResolutionModeVIP,
			Ports: []swarm.PortConfig{
				{TargetPort: uint32(portInt), PublishedPort: 0},
			},
		}
		networks = []swarm.NetworkAttachmentConfig{
			{Target: networkNameForApp(opts.app)},
		}
		extra := []string{extraRegisterCmds(opts.app)}
		cmds, _, err = dockercommon.LeanContainerCmdsWithExtra(opts.process, opts.image, opts.app, extra)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		var yamlData provision.TsuruYamlData
		yamlData, err = image.GetImageTsuruYamlData(opts.image)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		healthConfig = toHealthConfig(yamlData.Healthcheck, portInt)
	}
	restartCount := 0
	replicas := 0
	if opts.baseSpec != nil {
		replicas, err = strconv.Atoi(opts.baseSpec.Labels[labelProcessReplicas.String()])
		if err != nil && opts.baseSpec.Mode.Replicated != nil {
			replicas = int(*opts.baseSpec.Mode.Replicated.Replicas)
		}
		restartCount, _ = strconv.Atoi(opts.baseSpec.Labels[labelServiceRestart.String()])
	}
	if opts.processState.increment != 0 {
		replicas += opts.processState.increment
		if replicas < 0 {
			return nil, errors.New("cannot have less than 0 units")
		}
	} else if replicas == 0 && opts.processState.start {
		replicas = 1
	}
	routerName, err := opts.app.GetRouter()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	routerType, _, err := router.Type(routerName)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	srvName := serviceNameForApp(opts.app, opts.process)
	if opts.isDeploy {
		replicas = 1
		srvName = fmt.Sprintf("%s-build", srvName)
	}
	if opts.isIsolatedRun {
		replicas = 1
		srvName = fmt.Sprintf("%sisolated-run", srvName)
	}
	uReplicas := uint64(replicas)
	if opts.processState.stop {
		uReplicas = 0
	}
	if opts.processState.restart {
		restartCount++
	}
	labels := map[string]string{
		labelService.String():            strconv.FormatBool(true),
		labelServiceDeploy.String():      strconv.FormatBool(opts.isDeploy),
		labelServiceIsolatedRun.String(): strconv.FormatBool(opts.isIsolatedRun),
		labelServiceBuildImage.String():  opts.buildImage,
		labelAppName.String():            opts.app.GetName(),
		labelAppProcess.String():         opts.process,
		labelAppPlatform.String():        opts.app.GetPlatform(),
		labelRouterName.String():         routerName,
		labelRouterType.String():         routerType,
		labelProcessReplicas.String():    strconv.Itoa(replicas),
		labelServiceRestart.String():     strconv.Itoa(restartCount),
		labelPoolName.String():           opts.app.GetPool(),
		labelProvisionerName.String():    "swarm",
	}
	user, err := config.GetString("docker:user")
	if err != nil {
		user, _ = config.GetString("docker:ssh:user")
	}
	opts.constraints = append(opts.constraints, fmt.Sprintf("node.labels.%s == %s", labelNodePoolName, opts.app.GetPool()))
	spec := swarm.ServiceSpec{
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: swarm.ContainerSpec{
				Image:       opts.image,
				Env:         envs,
				Labels:      labels,
				Command:     cmds,
				User:        user,
				Healthcheck: healthConfig,
			},
			Networks: networks,
			RestartPolicy: &swarm.RestartPolicy{
				Condition: swarm.RestartPolicyConditionAny,
			},
			Placement: &swarm.Placement{
				Constraints: opts.constraints,
			},
		},
		Networks:     networks,
		EndpointSpec: endpointSpec,
		Annotations: swarm.Annotations{
			Name:   srvName,
			Labels: labels,
		},
		Mode: swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{
				Replicas: &uReplicas,
			},
		},
	}
	return &spec, nil
}