func (p *dockerProvisioner) deploy(a provision.App, imageId string, evt *event.Event) error { if err := checkCanceled(evt); err != nil { return err } containers, err := p.listContainersByApp(a.GetName()) if err != nil { return err } imageData, err := image.GetImageCustomData(imageId) if err != nil { return err } if len(containers) == 0 { toAdd := make(map[string]*containersToAdd, len(imageData.Processes)) for processName := range imageData.Processes { _, ok := toAdd[processName] if !ok { ct := containersToAdd{Quantity: 0} toAdd[processName] = &ct } toAdd[processName].Quantity++ } if err = setQuota(a, toAdd); err != nil { return err } _, err = p.runCreateUnitsPipeline(evt, a, toAdd, imageId, imageData.ExposedPort) } else { toAdd := getContainersToAdd(imageData, containers) if err = setQuota(a, toAdd); err != nil { return err } _, err = p.runReplaceUnitsPipeline(evt, a, toAdd, containers, imageId) } return err }
func (p *FakeProvisioner) RemoveUnit(app provision.App, name string) error { if err := p.getError("RemoveUnit"); err != nil { return err } index := -1 p.mut.Lock() defer p.mut.Unlock() pApp, ok := p.apps[app.GetName()] if !ok { return errNotProvisioned } for i, unit := range pApp.units { if unit.Name == name { index = i break } } if index == -1 { return errors.New("Unit not found.") } copy(pApp.units[index:], pApp.units[index+1:]) pApp.units = pApp.units[:len(pApp.units)-1] pApp.unitLen-- p.apps[app.GetName()] = pApp return nil }
func (p *dockerProvisioner) AddUnits(a provision.App, units uint, process string, w io.Writer) ([]provision.Unit, error) { if a.GetDeploys() == 0 { return nil, stderr.New("New units can only be added after the first deployment") } if units == 0 { return nil, stderr.New("Cannot add 0 units") } if w == nil { w = ioutil.Discard } writer := io.MultiWriter(w, &app.LogWriter{App: a}) imageId, err := appCurrentImageName(a.GetName()) if err != nil { return nil, err } conts, err := p.runCreateUnitsPipeline(writer, a, map[string]*containersToAdd{process: {Quantity: int(units)}}, imageId) routesRebuildOrEnqueue(a.GetName()) if err != nil { return nil, err } result := make([]provision.Unit, len(conts)) for i, c := range conts { result[i] = c.AsUnit(a) } return result, nil }
// gitDeployCmds returns the list of commands that are used when the // provisioner deploys a unit using the Git repository method. func gitDeployCmds(app provision.App, version string) ([]string, error) { repo, err := repository.Manager().GetRepository(app.GetName()) if err != nil { return nil, err } return deployCmds(app, "git", repo.ReadOnlyURL, version) }
func (*dockerProvisioner) RemoveUnits(a provision.App, units uint) error { if a == nil { return errors.New("remove units: app should not be nil") } if units < 1 { return errors.New("remove units: units must be at least 1") } containers, err := listContainersByAppOrderedByStatus(a.GetName()) if err != nil { return err } if units >= uint(len(containers)) { return errors.New("remove units: cannot remove all units from app") } var wg sync.WaitGroup for i := 0; i < int(units); i++ { wg.Add(1) go func(c container) { removeContainer(&c) wg.Done() }(containers[i]) } wg.Wait() return nil }
func (p *dockerProvisioner) deployPipeline(app provision.App, imageId string, commands []string, w io.Writer) (string, error) { actions := []*action.Action{ &insertEmptyContainerInDB, &createContainer, &startContainer, &updateContainerInDB, &followLogsAndCommit, } pipeline := action.NewPipeline(actions...) buildingImage, err := appNewImageName(app.GetName()) if err != nil { return "", log.WrapError(fmt.Errorf("error getting new image name for app %s", app.GetName())) } args := runContainerActionsArgs{ app: app, imageID: imageId, commands: commands, writer: w, isDeploy: true, buildingImage: buildingImage, provisioner: p, } err = pipeline.Execute(args) if err != nil { log.Errorf("error on execute deploy pipeline for app %s - %s", app.GetName(), err) return "", err } return buildingImage, nil }
func (p *dockerProvisioner) Destroy(app provision.App) error { containers, err := listContainersByApp(app.GetName()) if err != nil { log.Errorf("Failed to list app containers: %s", err.Error()) return err } var containersGroup sync.WaitGroup containersGroup.Add(len(containers)) for _, c := range containers { go func(c container) { defer containersGroup.Done() err := removeContainer(&c) if err != nil { log.Errorf("Unable to destroy container %s: %s", c.ID, err.Error()) } }(c) } containersGroup.Wait() err = removeImage(assembleImageName(app.GetName())) if err != nil { log.Errorf("Failed to remove image: %s", err.Error()) } r, err := getRouter() if err != nil { log.Errorf("Failed to get router: %s", err.Error()) return err } err = r.RemoveBackend(app.GetName()) if err != nil { log.Errorf("Failed to remove route backend: %s", err.Error()) return err } return nil }
func (p *swarmProvisioner) ArchiveDeploy(a provision.App, archiveURL string, evt *event.Event) (imgID string, err error) { baseImage := image.GetBuildImage(a) buildingImage, err := image.AppNewImageName(a.GetName()) if err != nil { return "", errors.WithStack(err) } client, err := chooseDBSwarmNode() if err != nil { return "", err } cmds := dockercommon.ArchiveDeployCmds(a, archiveURL) srvID, task, err := runOnceBuildCmds(client, a, cmds, baseImage, buildingImage, evt) if srvID != "" { defer removeServiceAndLog(client, srvID) } if err != nil { return "", err } _, err = commitPushBuildImage(client, buildingImage, task.Status.ContainerStatus.ContainerID, a) if err != nil { return "", err } err = deployProcesses(client, a, buildingImage, nil) if err != nil { return "", errors.WithStack(err) } return buildingImage, nil }
func (p *FakeProvisioner) RemoveUnits(app provision.App, n uint, process string, w io.Writer) error { if err := p.getError("RemoveUnits"); err != nil { return err } if n == 0 { return errors.New("cannot remove 0 units") } p.mut.Lock() defer p.mut.Unlock() pApp, ok := p.apps[app.GetName()] if !ok { return errNotProvisioned } var newUnits []provision.Unit removedCount := n for _, u := range pApp.units { if removedCount > 0 && u.ProcessName == process { removedCount-- continue } newUnits = append(newUnits, u) } if removedCount > 0 { return errors.New("too many units to remove") } if w != nil { fmt.Fprintf(w, "removing %d units", n) } pApp.units = newUnits pApp.unitLen = len(newUnits) p.apps[app.GetName()] = pApp return nil }
func (p *swarmProvisioner) ExecuteCommandIsolated(stdout, stderr io.Writer, a provision.App, cmd string, args ...string) error { if a.GetDeploys() == 0 { return errors.New("commands can only be executed after the first deploy") } img, err := image.AppCurrentImageName(a.GetName()) if err != nil { return err } client, err := chooseDBSwarmNode() if err != nil { return err } opts := tsuruServiceOpts{ app: a, image: img, isIsolatedRun: true, } cmds := []string{"/bin/bash", "-lc", cmd} cmds = append(cmds, args...) serviceID, _, err := runOnceCmds(client, opts, cmds, stdout, stderr) if serviceID != "" { removeServiceAndLog(client, serviceID) } return err }
func (p *dockerProvisioner) deployAndClean(a provision.App, imageId string, w io.Writer) error { err := p.deploy(a, imageId, w) if err != nil { p.cleanImage(a.GetName(), imageId) } return err }
func (p *FakeProvisioner) Provision(app provision.App) error { if err := p.getError("Provision"); err != nil { return err } if p.Provisioned(app) { return &provision.Error{Reason: "App already provisioned."} } p.mut.Lock() defer p.mut.Unlock() p.apps[app.GetName()] = provisionedApp{ app: app, unitLen: 1, units: []provision.Unit{ { Name: app.GetName() + "/0", AppName: app.GetName(), Type: app.GetPlatform(), Status: provision.StatusStarted, InstanceId: "i-080", Ip: "10.10.10.1", Machine: 1, }, }, } return nil }
func (p *JujuProvisioner) Provision(app provision.App) error { var buf bytes.Buffer charms, err := config.GetString("juju:charms-path") if err != nil { return errors.New(`Setting "juju:charms-path" is not defined.`) } args := []string{ "deploy", "--repository", charms, "local:" + app.GetPlatform(), app.GetName(), } err = runCmd(false, &buf, &buf, args...) out := buf.String() if err != nil { app.Log("Failed to create machine: "+out, "tsuru") return cmdError(out, err, args) } setOption := []string{ "set", app.GetName(), "app-repo=" + repository.ReadOnlyURL(app.GetName()), } runCmd(true, &buf, &buf, setOption...) if p.elbSupport() { router, err := Router() if err != nil { return err } if err = router.AddBackend(app.GetName()); err != nil { return err } p.enqueueUnits(app.GetName()) } return nil }
func (p *dockerProvisioner) Swap(app1, app2 provision.App) error { r, err := getRouterForApp(app1) if err != nil { return err } return r.Swap(app1.GetName(), app2.GetName()) }
func (JujuProvisioner) Swap(app1, app2 provision.App) error { r, err := Router() if err != nil { return err } return r.Swap(app1.GetName(), app2.GetName()) }
// getImage returns the image name or id from an app. // when the container image is empty is returned the platform image. // when a deploy is multiple of 10 is returned the platform image. func getImage(app provision.App) string { c, err := getOneContainerByAppName(app.GetName()) if err != nil || c.Image == "" || usePlatformImage(app) { return assembleImageName(app.GetPlatform()) } return c.Image }
func (p *dockerProvisioner) deploy(a provision.App, imageId string, w io.Writer) error { containers, err := p.listContainersByApp(a.GetName()) if err != nil { return err } imageData, err := getImageCustomData(imageId) if err != nil { return err } if len(containers) == 0 { toAdd := make(map[string]*containersToAdd, len(imageData.Processes)) for processName := range imageData.Processes { _, ok := toAdd[processName] if !ok { ct := containersToAdd{Quantity: 0} toAdd[processName] = &ct } toAdd[processName].Quantity++ } if err := setQuota(a, toAdd); err != nil { return err } _, err = p.runCreateUnitsPipeline(w, a, toAdd, imageId) } else { toAdd := getContainersToAdd(imageData, containers) if err := setQuota(a, toAdd); err != nil { return err } _, err = p.runReplaceUnitsPipeline(w, a, toAdd, containers, imageId) } routesRebuildOrEnqueue(a.GetName()) return err }
func (p *dockerProvisioner) Destroy(app provision.App) error { containers, _ := listContainersByApp(app.GetName()) go func(c []container) { var containersGroup sync.WaitGroup containersGroup.Add(len(containers)) for _, c := range containers { go func(c container) { defer containersGroup.Done() err := removeContainer(&c) if err != nil { log.Error(err.Error()) } }(c) } containersGroup.Wait() err := removeImage(assembleImageName(app.GetName())) if err != nil { log.Error(err.Error()) } }(containers) r, err := getRouter() if err != nil { log.Errorf("Failed to get router: %s", err) return err } return r.RemoveBackend(app.GetName()) }
func (p *swarmProvisioner) Destroy(a provision.App) error { client, err := chooseDBSwarmNode() if err != nil { return err } multiErrors := tsuruErrors.NewMultiError() processes, err := allAppProcesses(a.GetName()) if err != nil { multiErrors.Add(err) } for _, p := range processes { name := serviceNameForApp(a, p) err = client.RemoveService(docker.RemoveServiceOptions{ ID: name, }) if err != nil { if _, notFound := err.(*docker.NoSuchService); !notFound { multiErrors.Add(errors.WithStack(err)) } } } err = client.RemoveNetwork(networkNameForApp(a)) if err != nil { multiErrors.Add(errors.WithStack(err)) } if multiErrors.Len() > 0 { return multiErrors } return nil }
func (p *dockerProvisioner) deployAndClean(a provision.App, imageId string, evt *event.Event) error { err := p.deploy(a, imageId, evt) if err != nil { p.cleanImage(a.GetName(), imageId) } return err }
func deploy(app provision.App, version string, w io.Writer) (string, error) { commands, err := deployCmds(app, version) if err != nil { return "", err } imageId := getImage(app) actions := []*action.Action{&insertEmptyContainerInDB, &createContainer, &startContainer, &updateContainerInDB} pipeline := action.NewPipeline(actions...) err = pipeline.Execute(app, imageId, commands) if err != nil { log.Errorf("error on execute deploy pipeline for app %s - %s", app.GetName(), err) return "", err } c := pipeline.Result().(container) err = c.logs(w) if err != nil { log.Errorf("error on get logs for container %s - %s", c.ID, err) return "", err } _, err = dockerCluster().WaitContainer(c.ID) if err != nil { log.Errorf("Process failed for container %q: %s", c.ID, err) return "", err } imageId, err = c.commit() if err != nil { log.Errorf("error on commit container %s - %s", c.ID, err) return "", err } c.remove() return imageId, nil }
func (p *FakeProvisioner) AddUnit(app provision.App, unit provision.Unit) { p.mut.Lock() defer p.mut.Unlock() a := p.apps[app.GetName()] a.units = append(a.units, unit) a.unitLen++ p.apps[app.GetName()] = a }
func (p *JujuProvisioner) GitDeploy(a provision.App, version string, w io.Writer) error { var buf bytes.Buffer setOption := []string{"set", a.GetName(), "app-version=" + version} if err := runCmd(true, &buf, &buf, setOption...); err != nil { log.Errorf("juju: Failed to set app-version. Error: %s.\nCommand output: %s", err, &buf) } return deploy.Git(p, a, version, w) }
func (c *container) asUnit(a provision.App) provision.Unit { return provision.Unit{ Name: c.ID, AppName: a.GetName(), Type: a.GetPlatform(), Ip: c.HostAddr, Status: provision.StatusBuilding, } }
func (p *dockerProvisioner) deploy(a provision.App, imageId string, w io.Writer) error { containers, err := listContainersByApp(a.GetName()) if len(containers) == 0 { _, err = runCreateUnitsPipeline(w, a, 1) } else { _, err = runReplaceUnitsPipeline(w, a, containers) } return err }
// runWithAgentCmds returns the list of commands that should be passed when the // provisioner will run a unit using tsuru_unit_agent to start. // // This will only be called for legacy containers that have not been re- // deployed since the introduction of independent units per 'process' in // 0.12.0. func runWithAgentCmds(app provision.App) ([]string, error) { runCmd, err := config.GetString("docker:run-cmd:bin") if err != nil { return nil, err } host, _ := config.GetString("host") token := app.Envs()["TSURU_APP_TOKEN"].Value return []string{"tsuru_unit_agent", host, token, app.GetName(), runCmd}, nil }
func (p *FakeProvisioner) Addr(app provision.App) (string, error) { if err := p.getError("Addr"); err != nil { return "", err } pApp, ok := p.apps[app.GetName()] if !ok { return "", errNotProvisioned } return pApp.addr, nil }
func (p *dockerProvisioner) ImageDeploy(app provision.App, imageId string, w io.Writer) (string, error) { isValid, err := isValidAppImage(app.GetName(), imageId) if err != nil { return "", err } if !isValid { return "", fmt.Errorf("invalid image for app %s: %s", app.GetName(), imageId) } return imageId, p.deploy(app, imageId, w) }
func (*dockerProvisioner) RemoveUnit(a provision.App) error { if a == nil { return errors.New("remove unit: app should not be nil") } containers, err := listContainersByAppOrderedByStatus(a.GetName()) if err != nil { return err } return removeContainer(&containers[0]) }
func (p *FakeProvisioner) RoutableAddresses(app provision.App) ([]url.URL, error) { p.mut.Lock() defer p.mut.Unlock() units := p.apps[app.GetName()].units addrs := make([]url.URL, len(units)) for i := range units { addrs[i] = *units[i].Address } return addrs, nil }