func (e *RemoveDependerEnvDataForDependerAppExecutor) Execute(t *Task) error { if e.arg.App == "" { return errors.New("Please specify an app") } if e.arg.Depender == "" { return errors.New("Please specify a depender app") } if e.arg.Env == "" { return errors.New("Please specify an env") } zkApp, err := datamodel.GetApp(e.arg.App) if err != nil { e.reply.Status = StatusError return err } err = zkApp.RemoveDependerEnvDataForDependerApp(e.arg.Depender, e.arg.Env) if err != nil { e.reply.Status = StatusError return err } e.reply.Status = StatusOk castedApp := App(*zkApp) e.reply.App = &castedApp return err }
func (e *RegisterAppExecutor) Execute(t *Task) error { if e.arg.Name == "" { return errors.New("Please specify an app name to register") } if !AppRegexp.MatchString(e.arg.Name) { return errors.New("App name must be [A-Za-z0-9-]+") } if !e.arg.NonAtlantis && e.arg.Repo == "" { return errors.New("Please specify a repo") } if !e.arg.NonAtlantis && e.arg.Root == "" { return errors.New("Please specify the repo's root") } if e.arg.Email == "" { return errors.New("Please specify the email of the app owner") } if _, err := datamodel.GetApp(e.arg.Name); err == nil { return errors.New("Already Registered.") } _, err := datamodel.CreateOrUpdateApp(e.arg.NonAtlantis, e.arg.Internal, e.arg.Name, e.arg.Repo, e.arg.Root, e.arg.Email) if err != nil { e.reply.Status = StatusError } e.reply.Status = StatusOk return err }
func (e *AddDependerEnvDataForDependerAppExecutor) Execute(t *Task) error { if e.arg.App == "" { return errors.New("Please specify an app") } if e.arg.Depender == "" { return errors.New("Please specify a depender app") } if e.arg.DependerEnvData == nil { return errors.New("Please specify data for the env") } else if e.arg.DependerEnvData.Name == "" { return errors.New("Please specify name for the env") } // verify SecurityGroups are valid for ipGroup, _ := range e.arg.DependerEnvData.SecurityGroup { if _, err := datamodel.GetIPGroup(ipGroup); err != nil { return errors.New("Invalid IP Group in Security Group: " + ipGroup) } } zkApp, err := datamodel.GetApp(e.arg.App) if err != nil { e.reply.Status = StatusError return err } err = zkApp.AddDependerEnvDataForDependerApp(e.arg.Depender, e.arg.DependerEnvData) if err != nil { e.reply.Status = StatusError return err } e.reply.Status = StatusOk castedApp := App(*zkApp) e.reply.App = &castedApp return err }
func (e *GetAppEnvPortExecutor) Execute(t *Task) (err error) { if e.arg.App == "" { return errors.New("Please specify an app") } else if e.arg.Env == "" { return errors.New("Please specify an environment") } zkApp, err := datamodel.GetApp(e.arg.App) if err != nil { return err } zrp := datamodel.GetRouterPorts(zkApp.Internal) fmt.Printf("ROUTER PORTS: %+v\n", zrp) portStr := zrp.AppEnvMap[helper.GetAppEnvTrieName(e.arg.App, e.arg.Env)] if portStr == "" { return errors.New("port not found") } fmt.Printf("PORT STRING: %+v -> %+v\n", helper.GetAppEnvTrieName(e.arg.App, e.arg.Env), portStr) port, err := strconv.ParseUint(portStr, 10, 16) if err != nil { return err } helper.SetRouterRoot(zkApp.Internal) e.reply.Port, err = routerzk.GetPort(datamodel.Zk.Conn, uint16(port)) return err }
func (e *GetAppExecutor) Execute(t *Task) error { if e.arg.Name == "" { return errors.New("Please specify an app name to get") } app, err := datamodel.GetApp(e.arg.Name) if err != nil || app == nil { e.reply.Status = StatusError return errors.New("App " + e.arg.Name + " does not exist") } e.reply.Status = StatusOk castedApp := App(*app) e.reply.App = &castedApp return nil }
func (e *UnregisterAppExecutor) Execute(t *Task) error { if e.arg.Name == "" { return errors.New("Please specify an app name to unregister") } app, err := datamodel.GetApp(e.arg.Name) if err != nil || app == nil { e.reply.Status = StatusError return errors.New("App " + e.arg.Name + " does not exist") } if err = app.Delete(); err != nil { e.reply.Status = StatusError return err } e.reply.Status = StatusOk return nil }
func (e *GetDependerEnvDataExecutor) Execute(t *Task) error { if e.arg.App == "" { return errors.New("Please specify an app") } if e.arg.Env == "" { return errors.New("Please specify an env") } zkApp, err := datamodel.GetApp(e.arg.App) if err != nil { e.reply.Status = StatusError return err } data := zkApp.GetDependerEnvData(e.arg.Env, true) if err != nil { e.reply.Status = StatusError return err } e.reply.Status = StatusOk e.reply.DependerEnvData = data return err }
func (e *RequestAppDependencyExecutor) Execute(t *Task) error { if e.arg.App == "" { e.reply.Status = StatusError return errors.New("Please specify an app") } if e.arg.Dependency == "" { e.reply.Status = StatusError return errors.New("Please specify an app to depend on") } if len(e.arg.Envs) == 0 { e.reply.Status = StatusError return errors.New("Please specify the envs your app needs the dependency in") } for _, env := range e.arg.Envs { if _, err := datamodel.GetEnv(env); err != nil { e.reply.Status = StatusError return errors.New("The env " + env + " does not exist") } } // fetch apps zkApp, err := datamodel.GetApp(e.arg.App) if err != nil { e.reply.Status = StatusError return err } zkDep, err := datamodel.GetApp(e.arg.Dependency) if err != nil { e.reply.Status = StatusError return err } // check depender envs missingEnvs := []string{} if dad := zkDep.GetDependerAppData(e.arg.App, false); dad != nil { for _, env := range e.arg.Envs { if dad.DependerEnvData[env] == nil { missingEnvs = append(missingEnvs, env) } } } else { missingEnvs = e.arg.Envs } if len(missingEnvs) == 0 { return errors.New(fmt.Sprintf("Your app already has access to the dependency %s in envs %v", e.arg.Dependency, e.arg.Envs)) } // load template, format body, and set up subject subject := fmt.Sprintf("[Atlantis] '%s' is requesting '%s' as a dependency in envs %s", e.arg.App, e.arg.Dependency, strings.Join(e.arg.Envs, ",")) tmpl := template.Must(template.New("request_dependency").Parse(` {{.DepTeam}}, {{.User}} has requested that you add '{{.App}}' as a depender of your app '{{.Dependency}}' in the the environment(s) '{{.Envs}}'. Please visit this page to do so: https://{{.ManagerCName}}/static/dashboard/#addAppDepender/{{.Dependency}}/{{.App}}/{{.Envs}} If you wish to discuss this dependency, {{.AppTeam}} is included in this thread for your convenience. `)) myself, err := datamodel.GetManager(Region, Host) if err != nil { return err } buf := bytes.NewBuffer([]byte{}) tmpl.Execute(buf, RequestAppDependencyTemplate{ App: e.arg.App, AppTeam: strings.SplitN(zkApp.Email, "@", 2)[0], Dependency: e.arg.Dependency, DepTeam: strings.SplitN(zkDep.Email, "@", 2)[0], Envs: strings.Join(e.arg.Envs, ","), ManagerCName: myself.ManagerCName, User: e.arg.ManagerAuthArg.User, }) // send email requesting dependency if err := smtp.SendMail([]string{zkDep.Email, zkApp.Email}, subject, buf.String()); err != nil { e.reply.Status = StatusError return err } e.reply.Status = StatusOk return nil }
func (e *DeployExecutor) Execute(t *Task) error { // error checking if e.arg.App == "" { return errors.New("Please specify an app") } if e.arg.Sha == "" { return errors.New("Please specify a sha") } if e.arg.Env == "" { return errors.New("Please specify an environment") } if e.arg.CPUShares < 0 || (e.arg.CPUShares > 0 && e.arg.CPUShares != 1 && e.arg.CPUShares%CPUSharesIncrement != 0) { return errors.New(fmt.Sprintf("CPU Shares should be 1 or a multiple of %d", CPUSharesIncrement)) } if e.arg.MemoryLimit < 0 || (e.arg.MemoryLimit > 0 && e.arg.MemoryLimit%MemoryLimitIncrement != 0) { return errors.New(fmt.Sprintf("Memory should be a multiple of %d", MemoryLimitIncrement)) } // fetch the repo and root app, err := datamodel.GetApp(e.arg.App) if err != nil { return errors.New("App " + e.arg.App + " is not registered: " + err.Error()) } // fetch and parse manifest for app name manifestReader, err := builder.DefaultBuilder.Build(t, app.Repo, app.Root, e.arg.Sha) if err != nil { return errors.New("Build Error: " + err.Error()) } defer manifestReader.Close() t.LogStatus("Reading Manifest") data, err := bman.Read(manifestReader) if err != nil { return err } manifest, err := CreateManifest(data) if err != nil { return err } if e.arg.App != manifest.Name { // NOTE(edanaher): If we kick off two jobs simultaneously, they will assume they have the same job id, so // one of them will get the manifest from the other one's Jenkins job. Unfortunately, Jenkins doesn't // give back any sort of useful information when you create the job, so we can't just use an ID easily. // Moreover, the API may not in any way acknowledge the job for several seconds, meaning that we can't // even scan the jobs created during a brief time interval for the job in question. Rather, we have to // poll Jenkins until we find the job we're looking for. This is sufficiently terrible that I'm just // erroring out and blaming Jenkins rather than adding a giant pile of code to handle that case. We could // retry the job ourself, but after a day trying to beat Jenkins into submission, I have no interest in // applying further hacks. return errors.New("The app name you specified does not match the manifest. This is probably due to an unavoidable race condition in Jenkin's RESTless API. Please try again.") } if e.arg.CPUShares > 0 { manifest.CPUShares = e.arg.CPUShares } if e.arg.MemoryLimit > 0 { manifest.MemoryLimit = e.arg.MemoryLimit } // figure out how many instances we need if e.arg.Instances > 0 { manifest.Instances = e.arg.Instances } else if manifest.Instances == 0 { manifest.Instances = uint(1) // default to 1 instance } if e.arg.Dev { e.reply.Containers, err = devDeploy(&e.arg.ManagerAuthArg, manifest, e.arg.Sha, e.arg.Env, t) } else { e.reply.Containers, err = deploy(&e.arg.ManagerAuthArg, manifest, e.arg.Sha, e.arg.Env, t) } return err }
func ResolveDepValuesForZone(app string, zkEnv *datamodel.ZkEnv, zone string, names []string, encrypt bool, t *Task) (DepsType, error) { var ( err error suffix string ) deps := DepsType{} // if we're using DNS and the app is registered, try to get the app cname (if deployed) if dns.Provider != nil { suffix, err = dns.Provider.Suffix(Region) if err != nil { return deps, err } } for _, name := range names { // if app is registered for this dependency name zkApp, err := datamodel.GetApp(name) if err != nil { continue } appEnvData := zkApp.GetDependerEnvDataForDependerApp(app, zkEnv.Name, true) if appEnvData == nil { continue } envData := zkApp.GetDependerEnvData(zkEnv.Name, true) if envData == nil { envData = &DependerEnvData{Name: appEnvData.Name} } // merge the data mergedEnvData := MergeDependerEnvData(envData, appEnvData) appDep := &AppDep{ SecurityGroup: mergedEnvData.SecurityGroup, DataMap: mergedEnvData.DataMap, } if dns.Provider != nil && !zkApp.NonAtlantis && zkApp.Internal { // auto-populate Address port, created, err := datamodel.ReserveRouterPortAndUpdateTrie(zkApp.Internal, name, "", zkEnv.Name) if err != nil { return deps, err } if created { // add warning since this means that the app has not been deployed in this env yet t.AddWarning("App dependency " + name + " has not yet been deployed in environment " + zkEnv.Name) } if appDep.DataMap == nil { appDep.DataMap = map[string]interface{}{} } appDep.DataMap["address"] = helper.GetZoneRouterCName(true, zone, suffix) + ":" + port // auto-populate SecurityGroup portUint, err := strconv.ParseUint(port, 10, 16) if err != nil { return deps, err } appDep.SecurityGroup = map[string][]uint16{netsec.InternalRouterIPGroup: []uint16{uint16(portUint)}} } deps[name] = appDep } if encrypt { for _, value := range deps { crypto.EncryptAppDep(value) } } for _, name := range names { if _, ok := deps[name]; !ok { return deps, errors.New("Could not resolve dep " + name) } } return deps, nil }
func deployToHostsInZones(deps map[string]DepsType, manifest *Manifest, sha, env string, hosts map[string][]string, zones []string, t *Task) ([]*Container, error) { deployedContainers := []*Container{} // fetch the app zkApp, err := datamodel.GetApp(manifest.Name) if err != nil { return nil, err } // first check if zones have enough hosts for _, zone := range zones { // fail if zone has no hosts if hosts[zone] == nil || len(hosts[zone]) == 0 { return nil, errors.New(fmt.Sprintf("No hosts available for app %s in zone %s", manifest.Name, zone)) } } // now that we know that enough hosts are available t.LogStatus("Deploying to zones: %v", zones) respCh := make(chan *DeployZoneResult, len(zones)) for _, zone := range zones { go deployToZone(respCh, deps, manifest, sha, env, hosts[zone], zone) } numResults := 0 status := "Deployed to zones: " for result := range respCh { deployedContainers = append(deployedContainers, result.Containers...) if result.Error != nil { err = result.Error t.Log(err.Error()) status += result.Zone + ":FAIL " } else { status += result.Zone + ":SUCCESS " } t.LogStatus(status) numResults++ if numResults >= len(zones) { // we're done close(respCh) } } if err != nil { cleanup(false, deployedContainers, t) return nil, err } // set ports on zk supervisor - can't do this in parallel. we may deploy to the same host at the same time // and since we don't lock zookeeper (maybe we should), this would result in a race condition. t.LogStatus("Updating Zookeeper") for _, cont := range deployedContainers { datamodel.Supervisor(cont.Host).SetContainerAndPort(cont.ID, cont.PrimaryPort) } // we're good now, so lets move on t.LogStatus("Updating Router") deployedIDs := make([]string, len(deployedContainers)) count := 0 for _, cont := range deployedContainers { deployedIDs[count] = cont.ID count++ } err = datamodel.AddToPool(deployedIDs) if err != nil { // if we can't add the pool, clean up and fail cleanup(true, deployedContainers, t) return nil, errors.New("Update Pool Error: " + err.Error()) } if zkApp.Internal { // reserve router port if needed and add app+env _, _, err = datamodel.ReserveRouterPortAndUpdateTrie(zkApp.Internal, manifest.Name, sha, env) if err != nil { datamodel.DeleteFromPool(deployedIDs) cleanup(true, deployedContainers, t) return nil, errors.New("Reserve Router Port Error: " + err.Error()) } } else { // only update trie _, err = datamodel.UpdateAppEnvTrie(zkApp.Internal, manifest.Name, sha, env) if err != nil { datamodel.DeleteFromPool(deployedIDs) cleanup(true, deployedContainers, t) return nil, errors.New("Reserve Router Port Error: " + err.Error()) } } return deployedContainers, nil }