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 }
// CreateApp creates a new app. // // Creating a new app is a process composed of the following steps: // // 1. Save the app in the database // 2. Create the git repository using the repository manager // 3. Provision the app using the provisioner func CreateApp(app *App, user *auth.User) error { teams, err := user.Teams() if err != nil { return err } if len(teams) == 0 { return NoTeamsError{} } platform, err := getPlatform(app.Platform) if err != nil { return err } if platform.Disabled && !user.IsAdmin() { return InvalidPlatformError{} } var plan *Plan if app.Plan.Name == "" { plan, err = DefaultPlan() } else { plan, err = findPlanByName(app.Plan.Name) } if err != nil { return err } if app.TeamOwner == "" { if len(teams) > 1 { return ManyTeamsError{} } app.TeamOwner = teams[0].Name } err = app.ValidateTeamOwner(user) if err != nil { return err } app.Plan = *plan err = app.SetPool() if err != nil { return err } app.Teams = []string{app.TeamOwner} app.Owner = user.Email err = app.validate() if err != nil { return err } actions := []*action.Action{ &reserveUserApp, &insertApp, &exportEnvironmentsAction, &createRepository, &provisionApp, &setAppIp, } pipeline := action.NewPipeline(actions...) err = pipeline.Execute(app, user) if err != nil { return &AppCreationError{app: app.Name, Err: err} } return nil }
func addUserToTeam(w http.ResponseWriter, r *http.Request, t auth.Token) error { teamName := r.URL.Query().Get(":team") email := r.URL.Query().Get(":user") u, err := t.User() if err != nil { return err } rec.Log(u.Email, "add-user-to-team", "team="+teamName, "user="******"Team not found"} } if !team.ContainsUser(u) { msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name) return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } user, err := auth.GetUserByEmail(email) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: "User not found"} } actions := []*action.Action{ &addUserToTeamInRepositoryAction, &addUserToTeamInDatabaseAction, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(user, team) }
func moveOneContainer(c container, toHost string, errors chan error, wg *sync.WaitGroup, encoder *json.Encoder) { a, err := app.GetByName(c.AppName) defer wg.Done() if err != nil { errors <- err return } logProgress(encoder, "Moving unit %s for %q: %s -> %s...", c.ID, c.AppName, c.HostAddr, toHost) pipeline := action.NewPipeline( &provisionAddUnitToHost, &provisionRemoveOldUnit, ) err = pipeline.Execute(a, toHost, c) if err != nil { errors <- err return } logProgress(encoder, "Finished moving unit %s for %q.", c.ID, c.AppName) addedUnit := pipeline.Result().(provision.Unit) err = moveOneContainerInDB(a, c, addedUnit) if err != nil { errors <- err return } logProgress(encoder, "Moved unit %s -> %s for %s in DB.", c.ID, addedUnit.Name, c.AppName) }
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 }
// BindApp makes the bind between the service instance and an app. func (si *ServiceInstance) BindApp(app bind.App) error { actions := []*action.Action{ &addAppToServiceInstance, &setEnvironVariablesToApp, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(app, *si) }
func (p *dockerProvisioner) DeployPipeline() *action.Pipeline { actions := []*action.Action{ &app.ProvisionerDeploy, &app.IncrementDeploy, &app.BindService, } pipeline := action.NewPipeline(actions...) return pipeline }
func (s *S) TestRebalanceContainersDry(c *check.C) { p, err := s.startMultipleServersCluster() c.Assert(err, check.IsNil) err = s.newFakeImage(p, "tsuru/app-myapp", nil) c.Assert(err, check.IsNil) appInstance := provisiontest.NewFakeApp("myapp", "python", 0) defer p.Destroy(appInstance) p.Provision(appInstance) imageId, err := image.AppCurrentImageName(appInstance.GetName()) c.Assert(err, check.IsNil) args := changeUnitsPipelineArgs{ app: appInstance, toAdd: map[string]*containersToAdd{"web": {Quantity: 5}}, imageId: imageId, provisioner: p, toHost: "localhost", } pipeline := action.NewPipeline( &provisionAddUnitsToHost, &bindAndHealthcheck, &addNewRoutes, &setRouterHealthcheck, &updateAppImage, ) err = pipeline.Execute(args) c.Assert(err, check.IsNil) appStruct := &app.App{ Name: appInstance.GetName(), Pool: "test-default", } err = s.storage.Apps().Insert(appStruct) c.Assert(err, check.IsNil) router, err := getRouterForApp(appInstance) c.Assert(err, check.IsNil) beforeRoutes, err := router.Routes(appStruct.Name) c.Assert(err, check.IsNil) c.Assert(beforeRoutes, check.HasLen, 5) var serviceCalled bool rollback := s.addServiceInstance(c, appInstance.GetName(), nil, func(w http.ResponseWriter, r *http.Request) { serviceCalled = true w.WriteHeader(http.StatusOK) }) defer rollback() buf := safe.NewBuffer(nil) err = p.rebalanceContainers(buf, true) c.Assert(err, check.IsNil) c1, err := p.listContainersByHost("localhost") c.Assert(err, check.IsNil) c2, err := p.listContainersByHost("127.0.0.1") c.Assert(err, check.IsNil) c.Assert(c1, check.HasLen, 5) c.Assert(c2, check.HasLen, 0) routes, err := router.Routes(appStruct.Name) c.Assert(err, check.IsNil) c.Assert(routes, check.DeepEquals, beforeRoutes) c.Assert(serviceCalled, check.Equals, false) }
// BindApp makes the bind between the service instance and an app. func (si *ServiceInstance) BindApp(app bind.App, writer io.Writer) error { actions := []*action.Action{ &addAppToServiceInstance, &setBindAppAction, &setTsuruServices, &bindUnitsToServiceInstance, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(app, *si, writer) }
// AddUnits creates n new units within the provisioner, saves new units in the // database and enqueues the apprc serialization. func (app *App) AddUnits(n uint, process string, writer io.Writer) error { if n == 0 { return stderr.New("Cannot add zero units.") } err := action.NewPipeline( &reserveUnitsToAdd, &provisionAddUnits, ).Execute(app, n, writer, process) return err }
// AddCName adds a CName to app. It updates the attribute, // calls the SetCName function on the provisioner and saves // the app in the database, returning an error when it cannot save the change // in the database or add the CName on the provisioner. func (app *App) AddCName(cnames ...string) error { actions := []*action.Action{ &validateNewCNames, &setNewCNamesToProvisioner, &saveCNames, &updateApp, } err := action.NewPipeline(actions...).Execute(app, cnames) rebuild.RoutesRebuildOrEnqueue(app.Name) return err }
func deploy(app provision.App, commands []string, w io.Writer) (string, error) { imageId := getImage(app) actions := []*action.Action{&insertEmptyContainerInDB, &createContainer, &startContainer, &updateContainerInDB, &followLogsAndCommit} pipeline := action.NewPipeline(actions...) err := pipeline.Execute(app, imageId, commands, []string{}, w) if err != nil { log.Errorf("error on execute deploy pipeline for app %s - %s", app.GetName(), err) return "", err } return pipeline.Result().(string), nil }
func (app *App) RemoveCName(cnames ...string) error { actions := []*action.Action{ &checkCNameExists, &unsetCNameFromProvisioner, &removeCNameFromDatabase, &removeCNameFromApp, } err := action.NewPipeline(actions...).Execute(app, cnames) rebuild.RoutesRebuildOrEnqueue(app.Name) return err }
// AddUnits creates n new units within the provisioner, saves new units in the // database and enqueues the apprc serialization. func (app *App) AddUnits(n uint) error { if n == 0 { return stderr.New("Cannot add zero units.") } err := action.NewPipeline( &reserveUnitsToAdd, &provisionAddUnits, &saveNewUnitsInDatabase, ).Execute(app, n) return err }
func (p *dockerProvisioner) DeployPipeline() *action.Pipeline { actions := []*action.Action{ &app.ProvisionerDeploy, &app.IncrementDeploy, //&saveUnits, &injectEnvirons, &bindService, } pipeline := action.NewPipeline(actions...) return pipeline }
func (p *dockerProvisioner) Destroy(app provision.App) error { containers, err := p.listContainersByApp(app.GetName()) if err != nil { log.Errorf("Failed to list app containers: %s", err.Error()) return err } args := changeUnitsPipelineArgs{ app: app, toRemove: containers, writer: ioutil.Discard, provisioner: p, appDestroy: true, } pipeline := action.NewPipeline( &removeOldRoutes, &provisionRemoveOldUnits, &provisionUnbindOldUnits, ) err = pipeline.Execute(args) if err != nil { return err } images, err := listAppImages(app.GetName()) if err != nil { log.Errorf("Failed to get image ids for app %s: %s", app.GetName(), err.Error()) } cluster := p.Cluster() for _, imageId := range images { err = cluster.RemoveImage(imageId) if err != nil { log.Errorf("Failed to remove image %s: %s", imageId, err.Error()) } err = cluster.RemoveFromRegistry(imageId) if err != nil { log.Errorf("Failed to remove image %s from registry: %s", imageId, err.Error()) } } err = deleteAllAppImageNames(app.GetName()) if err != nil { log.Errorf("Failed to remove image names from storage for app %s: %s", app.GetName(), err.Error()) } r, err := getRouterForApp(app) 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 *dockerProvisioner) runReplaceUnitsPipeline(w io.Writer, a provision.App, toAdd map[string]*containersToAdd, toRemoveContainers []container.Container, imageId string, toHosts ...string) ([]container.Container, error) { var toHost string if len(toHosts) > 0 { toHost = toHosts[0] } if w == nil { w = ioutil.Discard } args := changeUnitsPipelineArgs{ app: a, toAdd: toAdd, toRemove: toRemoveContainers, toHost: toHost, writer: w, imageId: imageId, provisioner: p, } var pipeline *action.Pipeline if p.isDryMode { pipeline = action.NewPipeline( &provisionAddUnitsToHost, &provisionRemoveOldUnits, ) } else { pipeline = action.NewPipeline( &provisionAddUnitsToHost, &bindAndHealthcheck, &addNewRoutes, &removeOldRoutes, &updateAppImage, &provisionRemoveOldUnits, &provisionUnbindOldUnits, ) } err := pipeline.Execute(args) if err != nil { return nil, err } return pipeline.Result().([]container.Container), nil }
// Update changes informations of the application. func (app *App) Update(updateData App, w io.Writer) error { description := updateData.Description planName := updateData.Plan.Name poolName := updateData.Pool teamOwner := updateData.TeamOwner if description != "" { app.Description = description } if poolName != "" { app.Pool = poolName _, err := app.GetPoolForApp(app.Pool) if err != nil { return err } } conn, err := db.Conn() if err != nil { return err } defer conn.Close() if planName != "" { plan, err := findPlanByName(planName) if err != nil { return err } var oldPlan Plan oldPlan, app.Plan = app.Plan, *plan actions := []*action.Action{ &moveRouterUnits, &saveApp, &restartApp, &removeOldBackend, } err = action.NewPipeline(actions...).Execute(app, &oldPlan, w) if err != nil { return err } } if teamOwner != "" { team, err := auth.GetTeam(teamOwner) if err != nil { return err } app.TeamOwner = team.Name err = app.validateTeamOwner() if err != nil { return err } app.Grant(team) } return conn.Apps().Update(bson.M{"name": app.Name}, app) }
func (p *PipelineErrorFakeProvisioner) DeployPipeline() *action.Pipeline { act := action.Action{ Name: "error-pipeline", Forward: func(ctx action.FWContext) (action.Result, error) { return nil, errors.New("deploy error") }, Backward: func(ctx action.BWContext) { }, } actions := []*action.Action{&act} pipeline := action.NewPipeline(actions...) return pipeline }
func CreateServiceInstance(instance ServiceInstance, service *Service, user *auth.User) error { err := validateServiceInstanceName(service.Name, instance.Name) if err != nil { return err } instance.ServiceName = service.Name if instance.TeamOwner == "" { return ErrTeamMandatory } instance.Teams = []string{instance.TeamOwner} actions := []*action.Action{&createServiceInstance, &insertServiceInstance} pipeline := action.NewPipeline(actions...) return pipeline.Execute(*service, instance, user.Email) }
func (p *PipelineFakeProvisioner) DeployPipeline() *action.Pipeline { act := action.Action{ Name: "change-executed-pipeline", Forward: func(ctx action.FWContext) (action.Result, error) { p.executedPipeline = true return nil, nil }, Backward: func(ctx action.BWContext) { }, } actions := []*action.Action{&act} pipeline := action.NewPipeline(actions...) return pipeline }
// BindApp makes the bind between the service instance and an app. func (si *ServiceInstance) BindApp(app bind.App, writer io.Writer) error { args := bindPipelineArgs{ serviceInstance: si, app: app, writer: writer, } actions := []*action.Action{ &bindAppDBAction, &bindAppEndpointAction, &setBindedEnvsAction, &bindUnitsAction, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(&args) }
// ChangePlan changes the plan of the application. // // It may change the state of the application if the new plan includes a new // router or a change in the amount of available memory. func (app *App) ChangePlan(planName string, w io.Writer) error { plan, err := findPlanByName(planName) if err != nil { return err } var oldPlan Plan oldPlan, app.Plan = app.Plan, *plan actions := []*action.Action{ &moveRouterUnits, &saveApp, &restartApp, &removeOldBackend, } return action.NewPipeline(actions...).Execute(app, &oldPlan, w) }
// DeployApp calls the Provisioner.Deploy func DeployApp(app *App, version, commit string, writer io.Writer) error { start := time.Now() pipeline := Provisioner.DeployPipeline() if pipeline == nil { actions := []*action.Action{&ProvisionerDeploy, &IncrementDeploy} pipeline = action.NewPipeline(actions...) } logWriter := LogWriter{App: app, Writer: writer} err := pipeline.Execute(app, version, &logWriter) if err != nil { return err } elapsed := time.Since(start) return saveDeployData(app.Name, commit, elapsed) }
// BindApp makes the bind between the service instance and an app. func (si *ServiceInstance) BindApp(app bind.App, shouldRestart bool, writer io.Writer) error { args := bindPipelineArgs{ serviceInstance: si, app: app, writer: writer, shouldRestart: shouldRestart, } actions := []*action.Action{ bindAppDBAction, bindAppEndpointAction, setBoundEnvsAction, bindUnitsAction, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(&args) }
func runCreateUnitsPipeline(w io.Writer, a provision.App, toAddCount int) ([]container, error) { args := changeUnitsPipelineArgs{ app: a, unitsToAdd: toAddCount, writer: w, } pipeline := action.NewPipeline( &provisionAddUnitsToHost, &addNewRoutes, ) err := pipeline.Execute(args) if err != nil { return nil, err } return pipeline.Result().([]container), nil }
func (p *dockerProvisioner) start(oldContainer *container.Container, app provision.App, imageId string, w io.Writer, exposedPort string, destinationHosts ...string) (*container.Container, error) { commands, processName, err := dockercommon.LeanContainerCmds(oldContainer.ProcessName, imageId, app) if err != nil { return nil, err } var actions []*action.Action if oldContainer != nil && oldContainer.Status == provision.StatusStopped.String() { actions = []*action.Action{ &insertEmptyContainerInDB, &createContainer, &setContainerID, &stopContainer, &updateContainerInDB, &setNetworkInfo, } } else { actions = []*action.Action{ &insertEmptyContainerInDB, &createContainer, &setContainerID, &startContainer, &updateContainerInDB, &setNetworkInfo, } } pipeline := action.NewPipeline(actions...) args := runContainerActionsArgs{ app: app, processName: processName, imageID: imageId, commands: commands, destinationHosts: destinationHosts, provisioner: p, exposedPort: exposedPort, } err = pipeline.Execute(args) if err != nil { return nil, err } c := pipeline.Result().(container.Container) err = c.SetImage(p, imageId) if err != nil { return nil, err } return &c, nil }
// UnbindApp makes the unbind between the service instance and an app. func (si *ServiceInstance) UnbindApp(app bind.App, writer io.Writer) error { if si.FindApp(app.GetName()) == -1 { return &errors.HTTP{Code: http.StatusPreconditionFailed, Message: "This app is not bound to this service instance."} } args := bindPipelineArgs{ serviceInstance: si, app: app, writer: writer, } actions := []*action.Action{ &unbindUnits, &unbindAppDB, &unbindAppEndpoint, &removeBindedEnvs, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(&args) }
// UnbindApp makes the unbind between the service instance and an app. func (si *ServiceInstance) UnbindApp(app bind.App, shouldRestart bool, writer io.Writer) error { if si.FindApp(app.GetName()) == -1 { return ErrAppNotBound } args := bindPipelineArgs{ serviceInstance: si, app: app, writer: writer, shouldRestart: shouldRestart, } actions := []*action.Action{ &unbindUnits, &unbindAppDB, &unbindAppEndpoint, &removeBoundEnvs, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(&args) }
func start(app provision.App, imageId string, w io.Writer, destinationHosts ...string) (*container, error) { keyPair, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { return nil, err } privateKey, publicKey, err := marshalKey(keyPair) if err != nil { return nil, err } commands, err := runWithAgentCmds(app, publicKey) if err != nil { return nil, err } actions := []*action.Action{ &insertEmptyContainerInDB, &createContainer, &startContainer, &updateContainerInDB, &setNetworkInfo, } pipeline := action.NewPipeline(actions...) args := runContainerActionsArgs{ app: app, imageID: imageId, commands: commands, destinationHosts: destinationHosts, privateKey: privateKey, } err = pipeline.Execute(args) if err != nil { return nil, err } c := pipeline.Result().(container) err = c.setImage(imageId) if err != nil { return nil, err } err = c.setStatus(provision.StatusStarted.String()) if err != nil { return nil, err } return &c, nil }